CM3D2 Converter.model_import

   1import os
   2import math
   3import struct
   4import time
   5import traceback
   6import bpy
   7import bpy_extras
   8import bmesh
   9import mathutils
  10from collections import Counter
  11from . import common
  12from . import compat
  13from . import cm3d2_data
  14from .translations.pgettext_functions import *
  15from .misc_OBJECT_PT_transform import CNV_OT_align_to_cm3d2_base_bone
  16
  17
  18# メインオペレーター
  19@compat.BlRegister()
  20#@bpy_extras.io_utils.orientation_helper(axis_forward='-Z', axis_up='Y')
  21class CNV_OT_import_cm3d2_model(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
  22    bl_idname = 'import_mesh.import_cm3d2_model'
  23    bl_label = "CM3D2モデル (.model)"
  24    bl_description = "カスタムメイド3D2のmodelファイルを読み込みます"
  25    bl_options = {'REGISTER'}
  26
  27    filepath = bpy.props.StringProperty(subtype='FILE_PATH')
  28    filename_ext = ".model"
  29    filter_glob = bpy.props.StringProperty(default="*.model", options={'HIDDEN'})
  30
  31    scale = bpy.props.FloatProperty(name="倍率", default=5, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="インポート時のメッシュ等の拡大率です")
  32
  33    is_mesh = bpy.props.BoolProperty(name="メッシュ生成", default=True, description="ポリゴンを読み込みます、大抵の場合オンでOKです")
  34    is_remove_doubles = bpy.props.BoolProperty(name="重複頂点を結合", default=True, description="UVの切れ目でポリゴンが分かれている仕様なので、インポート時にくっつけます")
  35    is_seam = bpy.props.BoolProperty(name="シームをつける", default=True, description="UVの切れ目にシームをつけます")
  36    is_sharp = bpy.props.BoolProperty(name="Mark Sharp", default=True, description="This will mark removed doubles on your mesh as sharp (or all free edges if not removing doubles).")
  37
  38    is_convert_bone_weight_names = bpy.props.BoolProperty(name="頂点グループ名をBlender用に変換", default=False, description="全ての頂点グループ名をBlenderの左右対称編集で使えるように変換してから読み込みます")
  39    is_vertex_group_sort = bpy.props.BoolProperty(name="頂点グループを名前順ソート", default=True, description="頂点グループを名前順でソートします")
  40    is_remove_empty_vertex_group = bpy.props.BoolProperty(name="割り当てのない頂点グループを削除", default=True, description="全ての頂点に割り当てのない頂点グループを削除します")
  41
  42    reload_tex_cache = bpy.props.BoolProperty(name="テクスチャキャッシュを再構成", default=False, description="texファイルを探す際、キャッシュを再構成します")
  43    is_decorate = bpy.props.BoolProperty(name="種類に合わせてマテリアルを装飾", default=True)
  44    is_mate_data_text = bpy.props.BoolProperty(name="テキストにマテリアル情報埋め込み", default=True, description="シェーダー情報をテキストに埋め込みます")
  45
  46    is_armature = bpy.props.BoolProperty(name="アーマチュア生成", default=True, description="ウェイトを編集する時に役立つアーマチュアを読み込みます")
  47    is_armature_clean = bpy.props.BoolProperty(name="不要なボーンを削除", default=False, description="ウェイトが無いボーンを削除します")
  48    is_custom_bones = bpy.props.BoolProperty(name="Use Custom Bones", default=False, description="Use the currently selected object for custom bone shapes.")
  49    is_use_local_bones = bpy.props.BoolProperty(name="Use Local Bones", default=True, description="Use the Local Bone Data for orientation (more accurate)")
  50
  51    is_bone_data_text = bpy.props.BoolProperty(name="テキスト", default=True, description="ボーン情報をテキストとして読み込みます")
  52    is_bone_data_obj_property = bpy.props.BoolProperty(name="オブジェクトのカスタムプロパティ", default=True, description="メッシュオブジェクトのカスタムプロパティにボーン情報を埋め込みます")
  53    is_bone_data_arm_property = bpy.props.BoolProperty(name="アーマチュアのカスタムプロパティ", default=True, description="アーマチュアデータのカスタムプロパティにボーン情報を埋め込みます")
  54    texpath_dict = None
  55
  56    @classmethod
  57    def poll(cls, context):
  58        return True
  59
  60    def invoke(self, context, event):
  61        prefs = common.preferences()
  62        if prefs.model_default_path:
  63            self.filepath = common.default_cm3d2_dir(prefs.model_default_path, None, "model")
  64        else:
  65            self.filepath = common.default_cm3d2_dir(prefs.model_import_path, None, "model")
  66        self.scale = prefs.scale
  67        self.is_convert_bone_weight_names = prefs.is_convert_bone_weight_names
  68        if compat.IS_LEGACY or bpy.app.version < (2, 91):
  69            self.is_sharp = False
  70        context.window_manager.fileselect_add(self)
  71        return {'RUNNING_MODAL'}
  72
  73    def draw(self, context):
  74        prefs = common.preferences()
  75        self.layout.prop(self, 'scale')
  76
  77        box = self.layout.box()
  78        box.prop(self, 'is_mesh', icon='MESH_DATA')
  79
  80        sub_box = box.box()
  81        sub_box.enabled = self.is_mesh
  82        sub_box.label(text="メッシュ")
  83        sub_box.prop(self, 'is_remove_doubles', icon='STICKY_UVS_VERT')
  84        sub_box.prop(self, 'is_seam' , icon=compat.icon('UV_EDGESEL'))
  85        if not compat.IS_LEGACY and bpy.app.version >= (2, 91):
  86            sub_box.prop(self, 'is_sharp', icon=compat.icon('EDGESEL'))
  87
  88        sub_box = box.box()
  89        sub_box.enabled = self.is_mesh
  90        sub_box.label(text="頂点グループ")
  91        sub_box.prop(self, 'is_vertex_group_sort', icon='SORTALPHA')
  92        sub_box.prop(self, 'is_remove_empty_vertex_group', icon='DISCLOSURE_TRI_DOWN')
  93        sub_box.prop(self, 'is_convert_bone_weight_names', icon='BLENDER')
  94
  95        sub_box = box.box()
  96        sub_box.enabled = self.is_mesh
  97        sub_box.label(text="マテリアル")
  98        sub_box.prop(prefs, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
  99        sub_box.prop(self, 'reload_tex_cache', icon='FILE_REFRESH')
 100        if compat.IS_LEGACY:
 101            sub_box.prop(self, 'is_decorate', icon=compat.icon('SHADING_TEXTURE'))
 102        sub_box.prop(self, 'is_mate_data_text', icon='TEXT')
 103
 104        box = self.layout.box()
 105        box.prop(self, 'is_armature', icon='ARMATURE_DATA')
 106        
 107        sub_box = box.box()
 108        sub_box.label(text="アーマチュア")
 109        sub_box.prop(self , 'is_use_local_bones'          , icon=compat.icon('GROUP_BONE'), text="Use Local Bone Data")
 110        sub_box.prop(self , 'is_armature_clean'           , icon=compat.icon('X'         ))
 111        sub_box.prop(self , 'is_convert_bone_weight_names', icon=compat.icon('BLENDER'   ), text="ボーン名をBlender用に変換")
 112        sub_box.prop(prefs, 'show_bone_in_front'          , icon=compat.icon('HIDE_OFF'  ), text="Show Bones in Front")
 113        row = sub_box.row()
 114        row.prop    (self , 'is_custom_bones'             , icon=compat.icon('BONE_DATA' ), text="Use Selected as Bone Shape"     )
 115        row.enabled = bool(context.object)
 116        
 117        box = self.layout.box()
 118        box.label(text="ボーン情報埋め込み場所")
 119        box.prop(self, 'is_bone_data_text', icon='TEXT')
 120        box.prop(self, 'is_bone_data_obj_property', icon='OBJECT_DATA')
 121        box.prop(self, 'is_bone_data_arm_property', icon='ARMATURE_DATA')
 122
 123    def execute(self, context):
 124        start_time = time.time()
 125
 126        prefs = common.preferences()
 127        prefs.model_import_path = self.filepath
 128        prefs.scale = self.scale
 129        context.window_manager.progress_begin(0, 10)
 130        context.window_manager.progress_update(0)
 131
 132        custom_bone_ob = context.active_object
 133        if not custom_bone_ob:
 134            self.is_custom_bones = False
 135
 136        #global_matrix = bpy_extras.io_utils.axis_conversion(from_forward=self.axis_forward, from_up=self.axis_up).to_4x4()
 137
 138        try:
 139            reader = open(self.filepath, 'rb')
 140        except:
 141            self.report(type={'ERROR'}, message=f_tip_("ファイルを開くのに失敗しました、アクセス不可かファイルが存在しません。file={}", self.filepath))
 142            return {'CANCELLED'}
 143
 144        self.texpath_dict = common.get_texpath_dict(reload=self.reload_tex_cache)
 145
 146        with reader:
 147            # ヘッダー
 148            ext = None
 149            try: # luvoid : utf-8 decoding could possibly throw an error here
 150                ext = common.read_str(reader)
 151            except:
 152                ext = False
 153            if ext != 'CM3D2_MESH':
 154                self.report(type={'ERROR'}, message="これはカスタムメイド3D2のモデルファイルではありません")
 155                return {'CANCELLED'}
 156            model_ver = struct.unpack('<i', reader.read(4))[0]
 157            self.report(type={'INFO'}, message=f_tip_("Model Version = {version}", version=model_ver))
 158            context.window_manager.progress_update(0.1)
 159
 160            try:
 161                # 名前群を取得
 162                model_name1 = common.read_str(reader)
 163                model_name2 = common.read_str(reader)
 164                context.window_manager.progress_update(0.2)
 165
 166                # ボーン情報読み込み
 167                bone_data = []
 168                bone_count = struct.unpack('<i', reader.read(4))[0]
 169                for i in range(bone_count):
 170                    name = common.read_str(reader)
 171                    scl = struct.unpack('<B', reader.read(1))[0]
 172                    bone_data.append({'name': name, 'scl': scl})
 173
 174                for i in range(bone_count):
 175                    parent_index = struct.unpack('<i', reader.read(4))[0]
 176                    parent_name = None
 177                    if parent_index != -1:
 178                        parent_name = bone_data[parent_index]['name']
 179                    bone_data[i]['parent_index'] = parent_index
 180                    bone_data[i]['parent_name'] = parent_name
 181
 182                for i in range(bone_count):
 183                    x, y, z = struct.unpack('<3f', reader.read(3*4))
 184                    bone_data[i]['co'] = mathutils.Vector((x, y, z))
 185
 186                    x, y, z = struct.unpack('<3f', reader.read(3*4))
 187                    w = struct.unpack('<f', reader.read(4))[0]
 188                    bone_data[i]['rot'] = mathutils.Quaternion((w, x, y, z))
 189                    if model_ver >= 2001:
 190                        use_scale = struct.unpack('<B', reader.read(1))[0]
 191                        if use_scale:
 192                            print(bone_data[i]['name'],"has scale data!")
 193                            scale_x, scale_y, scale_z = struct.unpack('<3f', reader.read(3*4))
 194                            bone_data[i]['scale'] = [scale_x, scale_y, scale_z]
 195
 196                context.window_manager.progress_update(0.3)
 197
 198                print(f_("Reading vertex, mesh, and local bone count at 0x{num:02X}", num=reader.tell()))
 199                vertex_count, mesh_count, local_bone_count = struct.unpack('<3i', reader.read(3*4))
 200
 201                # ローカルボーン情報読み込み
 202                local_bone_data = []
 203                for i in range(local_bone_count):
 204                    local_bone_data.append({'name': common.read_str(reader)})
 205
 206                for i in range(local_bone_count):
 207                    row0 = struct.unpack('<4f', reader.read(4 * 4))
 208                    row1 = struct.unpack('<4f', reader.read(4 * 4))
 209                    row2 = struct.unpack('<4f', reader.read(4 * 4))
 210                    row3 = struct.unpack('<4f', reader.read(4 * 4))
 211                    local_bone_data[i]['matrix'] = mathutils.Matrix([row0, row1, row2, row3])
 212                context.window_manager.progress_update(0.4)
 213
 214                # 頂点情報読み込み
 215                vertex_data = []
 216                print(f_("Reading vertex data at 0x{num:02X}", num=reader.tell()))
 217                extra_uv_uses = [False] * 7
 218                if model_ver >= 2102: # CR Edit Mode
 219                    extra_uv_uses = struct.unpack('<7?', reader.read(7))
 220                    print(f_("extra_uv_uses = {boollist}", boollist=extra_uv_uses))
 221                for i in range(vertex_count):
 222                    co = struct.unpack('<3f', reader.read(3 * 4))
 223                    no = struct.unpack('<3f', reader.read(3 * 4))
 224                    uv = struct.unpack('<2f', reader.read(2 * 4))
 225                    extra_uvs = [] # CR Edit
 226                    for i, used in enumerate(extra_uv_uses):
 227                        if used:
 228                            extra_uvs.append(struct.unpack('<2f', reader.read(2 * 4)))
 229                    vertex_data.append({'co': co, 'normal': no, 'uv': uv, 'extra_uvs': extra_uvs})
 230                if self.is_remove_doubles:
 231                    comparison_data = list(hash(repr(v['co']) + " " + repr(v['normal'])) for v in vertex_data)
 232                    comparison_counter = Counter(comparison_data)
 233                    comparison_data = list((comparison_counter[h] > 1) for h in comparison_data)
 234                    del comparison_counter
 235                print(f_("Reading unknown count at 0x{num:02X}", num=reader.tell()))
 236                unknown_count = struct.unpack('<i', reader.read(4))[0]
 237                for i in range(unknown_count):
 238                    struct.unpack('<4f', reader.read(4 * 4))
 239                for i in range(vertex_count):
 240                    indexes = struct.unpack('<4H', reader.read(4 * 2))
 241                    values = struct.unpack('<4f', reader.read(4 * 4))
 242                    vertex_data[i]['weights'] = list({
 243                            'index': index,
 244                            'value': value,
 245                            'name': local_bone_data[index]['name'],
 246                        } for index, value in zip(indexes, values))
 247                context.window_manager.progress_update(0.5)
 248                # 面情報読み込み
 249                face_data = []
 250                for i in range(mesh_count):
 251                    face_count = int(struct.unpack('<i', reader.read(4))[0] / 3)
 252                    datum = [tuple(reversed(struct.unpack('<3H', reader.read(3 * 2)))) for j in range(face_count)]
 253                    face_data.append(datum)
 254                context.window_manager.progress_update(0.6)
 255
 256                # マテリアル情報読み込み
 257                # TODO MaterialHandlerに変更
 258                material_data = []
 259                material_count = struct.unpack('<i', reader.read(4))[0]
 260                for i in range(material_count):
 261                    print(f_("mate count: {num} of {count} @ 0x{pos:02X}", num=i, count=material_count, pos=reader.tell()))
 262                    data = cm3d2_data.MaterialHandler.read(reader, read_header=False, version=model_ver)
 263                    data.name1 = data.name.lower()
 264                    material_data.append(data)
 265                    
 266                    # name1 = common.read_str(reader)
 267                    # name2 = common.read_str(reader)
 268                    # name3 = common.read_str(reader)
 269                    # data_list = []
 270                    # material_data.append({'name1': name1, 'name2': name2, 'name3': name3, 'data': data_list})
 271                    # while True:
 272                    #     data_type = common.read_str(reader)
 273                    #     if data_type == 'tex':
 274                    #         data_item = {'type': data_type}
 275                    #         data_list.append(data_item)
 276                    #         data_item['name'] = common.read_str(reader)
 277                    #         data_item['type2'] = common.read_str(reader)
 278                    #         if data_item['type2'] == 'tex2d':
 279                    #             data_item['name2'] = common.read_str(reader)
 280                    #             data_item['path'] = common.read_str(reader)
 281                    #             data_item['tex_map'] = struct.unpack('<4f', reader.read(4*4))
 282                    #     elif data_type == 'col':
 283                    #         name = common.read_str(reader)
 284                    #         col = struct.unpack('<4f', reader.read(4*4))
 285                    #         data_list.append({'type': data_type, 'name': name, 'color': col})
 286                    #     elif data_type == 'f':
 287                    #         name = common.read_str(reader)
 288                    #         fval = struct.unpack('<f', reader.read(4))[0]
 289                    #         data_list.append({'type': data_type, 'name': name, 'float': fval})
 290                    #     else:
 291                    #         break
 292
 293                context.window_manager.progress_update(0.8)
 294
 295                # その他情報読み込み
 296                misc_data = []
 297                while True:
 298                    #print(f_("Reading data_type at 0x{num:02X}", num=reader.tell()))
 299                    data_type = common.read_str(reader)
 300                    if data_type == 'morph':
 301                        misc_item = {'type': data_type}
 302                        misc_data.append(misc_item)
 303                        misc_item['name'] = common.read_str(reader)
 304                        misc_item['data'] = data_list = []
 305                        morph_vert_count = struct.unpack('<i', reader.read(4))[0]
 306                        morph_extra_uvs = False
 307                        if model_ver >= 2102: # CR Edit Mode
 308                            morph_extra_uvs = struct.unpack('<?', reader.read(1))[0]
 309                            misc_item['uvs'] = []
 310                            print(f_("{morph}.morph_extra_uvs @ 0x{pos:02X} = {bool}", morph=misc_item['name'], bool=morph_extra_uvs, pos=reader.tell()-1))
 311                        for i in range(morph_vert_count):
 312                            index = struct.unpack('<H', reader.read(2))[0]
 313                            co = mathutils.Vector(struct.unpack('<3f', reader.read(3 * 4)))
 314                            normal = struct.unpack('<3f', reader.read(3 * 4))
 315                            extra_uvs = () # CR Edit
 316                            if morph_extra_uvs:
 317                                extra_uvs = struct.unpack('<4f', reader.read(4 * 4))
 318                            data_list.append({'index': index, 'co': co, 'normal': normal, 'color': extra_uvs})
 319                    else:
 320                        break
 321            
 322            except UnicodeDecodeError as e:
 323                msg = [
 324                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()-len(e.object)) + "\n",
 325                    str(e) + "\n",
 326                    *traceback.format_tb(e.__traceback__)
 327                ]
 328                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
 329                print("".join(msg))
 330                return {'CANCELLED'}
 331            
 332            except struct.error as e:
 333                msg = [
 334                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()) + "\n",
 335                    str(e) + "\n",
 336                    *traceback.format_tb(e.__traceback__)
 337                ]
 338                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
 339                print("".join(msg))
 340                return {'CANCELLED'}
 341
 342            except common.CM3D2ImportException as e:
 343                msg = [
 344                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()) + "\n",
 345                    str(e) + "\n",
 346                    *traceback.format_tb(e.__traceback__)
 347                ]
 348                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
 349                print("".join(msg))
 350                return {'CANCELLED'}
 351
 352        context.window_manager.progress_update(1)
 353
 354        try:
 355            bpy.ops.object.mode_set(mode='OBJECT')
 356        except RuntimeError:
 357            pass
 358        bpy.ops.object.select_all(action='DESELECT')
 359
 360        # アーマチュア作成
 361        if self.is_armature:
 362            arm    = bpy.data.armatures.new(model_name1 + ".armature")
 363            arm_ob = bpy.data.objects.new  (model_name1 + ".armature", arm)
 364            compat.link(bpy.context.scene, arm_ob)
 365            compat.set_select(arm_ob, True)
 366            compat.set_active(context, arm_ob)
 367
 368            arm.show_names              = prefs.show_bone_names        
 369            arm.show_axes               = prefs.show_bone_axes         
 370            arm.show_bone_custom_shapes = prefs.show_bone_custom_shapes
 371            arm.show_group_colors       = prefs.show_bone_group_colors
 372            if compat.IS_LEGACY:
 373                arm_ob.show_x_ray = prefs.show_bone_in_front
 374            else:
 375                arm_ob.show_in_front = prefs.show_bone_in_front     
 376
 377            bpy.ops.object.mode_set(mode='EDIT')
 378
 379            is_odd_scale_bone = False
 380
 381            # 基幹ボーンのみ作成
 382            child_data = []
 383            for data in bone_data:
 384                if not data['parent_name']:
 385                    bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 386                    bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
 387                    bone.use_deform = False
 388
 389                    #co.x, co.y, co.z = -co.x, co.z, -co.y
 390                    #rot = compat.mul(rot, mathutils.Quaternion((0, 0, 1), math.radians(90)))
 391                    #rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, rot.z, -rot.y
 392                    
 393                    #co  = data['co' ].copy()
 394                    #rot = data['rot'].copy()
 395                    #co.x, co.y, co.z = -co.x, -co.z, co.y
 396                    #rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, -rot.z, rot.y
 397                    ##rot = compat.mul(rot, mathutils.Quaternion((0, 0, 1), math.radians(-90)))
 398                    #rot = compat.convert_cm_to_bl_bone_rotation(rot)
 399                    #mat = compat.mul(mathutils.Matrix.Translation(co), rot.to_matrix().to_4x4())
 400                    
 401                    co_mat  = mathutils.Matrix.Translation(data['co'].copy() * self.scale)
 402                    rot     = mathutils.Quaternion(data['rot'].copy())
 403                    #rot     = compat.convert_cm_to_bl_bone_rotation(rot)
 404                    rot_mat = rot.to_matrix().to_4x4()
 405                    #rot_mat = compat.convert_cm_to_bl_bone_rotation(rot_mat)
 406                    mat = compat.mul(co_mat, rot_mat)
 407                    mat = compat.convert_cm_to_bl_bone_rotation(mat)
 408                    mat = compat.convert_cm_to_bl_space(mat)
 409                    #mat = compat.mul(mat, compat.CM_TO_BL_LOCAL_BONE_MAT4)
 410                    
 411                    
 412                    #fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
 413                    #fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
 414                    #fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
 415
 416                    #compat.set_bone_matrix(bone, compat.mul4(fix_mat_scale, fix_mat_before, mat, fix_mat_after))
 417                    compat.set_bone_matrix(bone, mat)
 418
 419
 420                    bone["cm3d2_scl_bone"] = 1 if data['scl'] else 0
 421                    if 'scale' in data:
 422                        bone['cm3d2_bone_scale'] = data['scale']
 423                        scale = mathutils.Vector(data['scale'])
 424                        if ( scale - mathutils.Vector((1,1,1)) ).length > 1e-5:
 425                            is_odd_scale_bone = True
 426                            self.report(type={'WARNING'}, message=f_tip_("Bone '{bone_name}' has odd scale '{bone_scale}' (odd by {bone_diff})", bone_name=bone.name, bone_scale=scale, bone_diff=( scale - mathutils.Vector((1,1,1)) ).length))
 427                        scale *= self.scale * 0.01
 428                        scale = compat.convert_cm_to_bl_bone_rotation(scale)
 429                        bone.bbone_x = scale.x
 430                        bone.bbone_z = scale.z
 431                        #look = bone.tail - bone.head
 432                        #look *= scale.y
 433                        #bone.tail = look + bone.head
 434                else:
 435                    child_data.append(data)
 436            context.window_manager.progress_update(1.333)
 437
 438            # 子ボーンを追加していく
 439            while len(child_data):
 440                data = child_data.pop(0)
 441                parent = arm.edit_bones.get(common.decode_bone_name(data['parent_name'], self.is_convert_bone_weight_names))
 442                if parent:
 443                    bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 444                    bone.parent = parent
 445                    bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
 446                    bone.use_deform = False
 447
 448                    #parent_mats = []
 449                    #current_bone = bone
 450                    #while current_bone:
 451                    #    for b in bone_data:
 452                    #        if common.decode_bone_name(b['name'], self.is_convert_bone_weight_names) == current_bone.name:
 453                    #            local_co  = b['co' ].copy()
 454                    #            local_rot = b['rot'].copy()
 455                    #            break
 456                    #
 457                    #    local_co_mat  = mathutils.Matrix.Translation(local_co)
 458                    #    local_rot_mat = local_rot.to_matrix().to_4x4()        
 459                    #    parent_mats.append(compat.mul(local_co_mat, local_rot_mat))
 460                    #
 461                    #    current_bone = current_bone.parent
 462                    #parent_mats.reverse()
 463                    #
 464                    #mat = mathutils.Matrix()
 465                    #for local_mat in parent_mats:
 466                    #    mat = compat.mul(mat, local_mat)
 467                    #mat *= self.scale
 468                    #mat = compat.convert_cm_to_bl_space(mat)
 469                    #mat = compat.convert_cm_to_bl_bone_rotation(mat)
 470                     
 471                    #parent_mat    = compat.mul(
 472                    #    mathutils.Matrix.Translation(parent.matrix.to_translation()),
 473                    #    parent.matrix.to_quaternion().to_matrix().to_4x4()
 474                    #)
 475
 476                    parent_mat = parent.matrix
 477                    
 478                    local_co      = data['co' ].copy() * self.scale
 479                    local_rot     = data['rot'].copy()
 480                    #local_rot     = compat.convert_cm_to_bl_bone_rotation(rot)
 481                    local_co_mat  = mathutils.Matrix.Translation(local_co)
 482                    local_rot_mat = local_rot.to_matrix().to_4x4()
 483                    local_mat     = compat.mul(local_co_mat, local_rot_mat)
 484                    local_mat     = compat.convert_cm_to_bl_bone_space(local_mat)
 485                    #local_mat     = compat.mul(local_mat, mathutils.Matrix.Diagonal((1,1,1)).to_4x4())
 486                    mat = compat.mul(parent_mat, local_mat)
 487                    mat = compat.convert_cm_to_bl_bone_rotation(mat)
 488
 489                    
 490                    #co_mat        = compat.mul( parent.matrix.inverted(), compat.convert_cm_to_bl_local_bone_mat4(local_co_mat) )
 491                    #rot_mat       = compat.mul( local_rot_mat, compat.CM_TO_BL_LOCAL_BONE_MAT4 )
 492                    #mat           = compat.ul(co_mat, rot_mat)
 493                    #local_mat = compat.mul(local_co_mat, local_rot_mat)
 494                    #local_mat *= self.scale
 495                    #mat = compat.mul( parent.matrix, compat.convert_cm_to_bl_local_bone(local_mat) )
 496                    #mat *= self.scale
 497
 498                    #mat *= self.scale
 499
 500                    #co.x, co.y, co.z = -co.y, co.z, co.x
 501                    #rot.w, rot.x, rot.y, rot.z = rot.w, rot.y, -rot.z, -rot.x
 502
 503                    #co  = data['co' ].copy() * self.scale
 504                    #rot = data['rot'].copy()
 505                    #co.x, co.y, co.z = co.z, -co.x, co.y
 506                    ##co = compat.convert_cm_to_bl_local_bone(co)
 507                    ##rot.w, rot.x, rot.y, rot.z = rot.w, -rot.z, rot.x, -rot.y 
 508                    ##rot.w, rot.x, rot.y, rot.z = rot.w, -rot.z, rot.x, -rot.y
 509                    #local_mat = compat.mul(mathutils.Matrix.Translation(co), rot.to_matrix().to_4x4())
 510                    #mat = compat.mul( parent.matrix, local_mat )
 511                    ##mat *= self.scale
 512
 513                    #fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
 514                    #fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
 515                    #fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
 516
 517                    #compat.set_bone_matrix(bone, compat.mul4(fix_mat_scale, fix_mat_before, mat, fix_mat_after))
 518                    compat.set_bone_matrix(bone, mat)
 519                    
 520                    bone['cm3d2_scl_bone'] = 1 if data['scl'] else 0
 521                    if 'scale' in data:
 522                        bone['cm3d2_bone_scale'] = data['scale']
 523                        scale = mathutils.Vector(data['scale'])
 524                        if ( scale - mathutils.Vector((1,1,1)) ).length > 1e-5:
 525                            is_odd_scale_bone = True
 526                            self.report(type={'WARNING'}, message=f_tip_("Bone '{bone_name}' has odd scale '{bone_scale}' (odd by {bone_diff})", bone_name=bone.name, bone_scale=scale, bone_diff=( scale - mathutils.Vector((1,1,1)) ).length))
 527                        scale *= self.scale * 0.01
 528                        bone.bbone_x = scale.x
 529                        bone.bbone_z = scale.z
 530                        #bone.bbone_segments = scale.y
 531                        #look = bone.tail - bone.head
 532                        #look *= scale.y
 533                        #bone.tail = look + bone.head
 534                else:
 535                    child_data.append(data)
 536            context.window_manager.progress_update(1.666)
 537            
 538            # Configure bones in local bone data
 539            is_local_bones_corrupt = False
 540            base_bone = arm.edit_bones.get(common.decode_bone_name(model_name2, self.is_convert_bone_weight_names))
 541            base_bone_offset = base_bone.matrix.copy()
 542            base_bone_offset = compat.mul(mathutils.Matrix.Scale(-1, 4, (1, 0, 0)), base_bone_offset)
 543            base_bone_offset = compat.convert_bl_to_cm_bone_rotation(base_bone_offset)
 544            print(base_bone_offset)
 545            print(f"base_bone_offset @ I =\n{base_bone_offset @ mathutils.Matrix.Identity(4)}")
 546            #base_bone_offset = mathutils.Matrix.Identity(4) # compat.mul(base_bone_mat.inverted(), base_bone_mat)
 547
 548            def setup_local_bone(bone, mat, isRoot=False):
 549                pos = compat.transform_inverse(mat.transposed()).translation
 550                mat.row[3] = (0.0, 0.0, 0.0, 1.0)
 551                mat.translation = pos
 552                mat.translation *= self.scale
 553                offset_mat = mat.copy()
 554                if not common.is_descendant_of(bone, base_bone):
 555                    mat = compat.mul(base_bone_offset, mat)
 556                    #mat.translation = mat.translation + base_bone_offset.translation
 557                mat = compat.convert_cm_to_bl_bone_rotation(mat)
 558                mat = compat.mul(mathutils.Matrix.Scale(-1, 4, (1, 0, 0)), mat)
 559                
 560            
 561                # The matrices from the local bone data are more precise rotations, but make sure they aren't corrupted
 562                old_pos, old_rot, old_scale = bone.matrix.decompose()
 563                compat.set_bone_matrix(bone, mat)
 564                new_pos, new_rot, new_scale = bone.matrix.decompose()
 565                dif_pos = (new_pos-old_pos).length/self.scale
 566                dif_rot = old_rot.rotation_difference(new_rot)
 567                if dif_pos > 0.1 or dif_rot.w < .9:
 568                    print(dif_pos,  dif_rot)
 569                    is_local_bones_corrupt = True
 570                    #self.report(type={'WARNING'}, message="Found potentially corrupt local bone data, please re-import with \"Use Local Bone Data\" disabled.")
 571                return mat
 572
 573            for data in local_bone_data:
 574                if self.is_use_local_bones and data['name'] == model_name2:
 575                    bone = arm.edit_bones.get(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 576                    mat = mathutils.Matrix(data['matrix'])
 577                    print("Found base bone in local bone data!")
 578                    #base_bone_offset = compat.mul(compat.transform_inverse(base_bone_offset), setup_local_bone(bone, mat, isRoot=True))
 579
 580
 581            for data in local_bone_data:
 582                bone = arm.edit_bones.get(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 583                bone.use_deform = True
 584                if self.is_use_local_bones and not data['name'] == model_name2:
 585                    mat = mathutils.Matrix(data['matrix'])
 586                    setup_local_bone(bone, mat)
 587            
 588            
 589            def distOnRay(pos0, pos1, point):
 590                w = point - pos0
 591                d = (pos1 - pos0).normalized()
 592                return w.dot(d) / d.dot(d)
 593            
 594            # ボーン整頓
 595            for bone in arm.edit_bones:
 596                if len(bone.children) == 0:
 597                    if bone.parent:
 598                        bone.length = bone.parent.length * 0.5
 599                    else:
 600                        bone.length = 0.2 * self.scale
 601                elif len(bone.children) == 1:
 602                    co = bone.children[0].head - bone.head
 603                    bone.length = co.length
 604                elif len(bone.children) >= 2:
 605                    if bone.parent:
 606                        max_len = 0.0
 607                        for child_bone in bone.children:
 608                            if "Pelvis" in bone.name:
 609                                dist = (child_bone.head - bone.head).length
 610                            else:
 611                                dist = distOnRay(bone.head, bone.tail, child_bone.head)
 612                            if dist > max_len:
 613                                max_len = dist
 614                        bone.length = max_len
 615                    else:
 616                        bone.length = 0.2 * self.scale
 617            for bone in arm.edit_bones:
 618                if len(bone.children) == 0:
 619                    if bone.parent:
 620                        bone.length = bone.parent.length * 0.5
 621            
 622            # Make sure no bones are length 0, otherwise blender deletes them
 623            for bone in arm.edit_bones:
 624                min_length = 0.0001
 625                if bone.length < min_length:
 626                    bone.length = min_length
 627            
 628            # 一部ボーン削除
 629            if self.is_armature_clean:
 630                for bone in arm.edit_bones:
 631                    for b in local_bone_data:
 632                        name = common.decode_bone_name(b['name'], self.is_convert_bone_weight_names)
 633                        if bone.name == name:
 634                            break
 635                    else:
 636                        arm.edit_bones.remove(bone)
 637
 638            arm.layers[16] = True
 639            compat.set_display_type(arm, prefs.bone_display_type)
 640            bpy.ops.armature.select_all(action='DESELECT')
 641            bpy.ops.object.mode_set(mode='OBJECT')
 642            if self.is_custom_bones:
 643                print("Set custom bones")
 644                for pose_bone in arm_ob.pose.bones:
 645                    pose_bone.custom_shape = custom_bone_ob
 646        context.window_manager.progress_update(2)
 647
 648        if self.is_mesh:
 649            ob, me = self.create_mesh(context, model_name1, vertex_data, face_data)
 650            # オブジェクト変形
 651            CNV_OT_align_to_cm3d2_base_bone.from_bone_data(ob, bone_data, local_bone_data, base_bone_name=model_name2, scale=self.scale)
 652            context.window_manager.progress_update(3)
 653            
 654            self.create_uvs(context, me, vertex_data, extra_uv_uses)
 655            context.window_manager.progress_update(4)
 656
 657            self.create_vertex_groups(context, ob, vertex_data, local_bone_data)
 658            context.window_manager.progress_update(5)
 659
 660            self.create_shapekeys(context, ob, misc_data)
 661            context.window_manager.progress_update(6)
 662
 663            # マテリアル追加
 664            progress_count_total = 0.0
 665            for data in material_data:
 666                progress_count_total += 1 #len(data['data'])
 667            self.progress_plus_value = 1.0 / (progress_count_total if progress_count_total > 0.0 else 1.0)
 668            self.progress_count = 6.0
 669
 670            face_seek = 0
 671            mates_set = set()
 672            override = context.copy()
 673            override['object'] = ob
 674            prefs = common.preferences()
 675            for index, data in enumerate(material_data):
 676                print(f_("material count: {num} of {count}", num=index, count=material_count))
 677                if prefs.mate_unread_same_value and data.name in mates_set:
 678                    continue
 679                mates_set.add(data.name)
 680                #common.preferences().mate_unread_same_value
 681                bpy.ops.object.material_slot_add(override)
 682                mate = context.blend_data.materials.new(data.name)#['name1'])
 683                #mate['shader1'] = data['name2']
 684                #mate['shader2'] = data['name3']
 685
 686                ob.material_slots[-1].material = mate
 687                # 面にマテリアル割り当て
 688                for i in range(face_seek, face_seek + len(face_data[index])):
 689                    me.polygons[i].material_index = index
 690                face_seek += len(face_data[index])
 691
 692                # テクスチャ追加
 693                if compat.IS_LEGACY:
 694                    #self.create_mateprop_old(context, me, texes_set, mate, index, data)
 695                    cm3d2_data.MaterialHandler.apply_to_old(override, mate, data)
 696                    common.decorate_material(mate, self.is_decorate, me, index)
 697                else:
 698                    #self.create_mateprop(context, me, texes_set, mate, index, data)
 699                    cm3d2_data.MaterialHandler.apply_to(override, mate, data)
 700                    common.decorate_material(mate, self.is_decorate, me, index)
 701                common.setup_material(mate)
 702
 703            ob.active_material_index = 0
 704            context.window_manager.progress_update(7)
 705
 706            # メッシュ整頓
 707            pre_mesh_select_mode = context.tool_settings.mesh_select_mode[:]
 708            
 709            # Too buggy on versions before 2.91 so just disable it outright
 710            #if self.is_sharp and (compat.IS_LEGACY or bpy.app.version < (2, 91)):
 711            #    context.tool_settings.mesh_select_mode = (False, True, False)
 712            #    bpy.ops.object.mode_set(mode='EDIT')
 713            #    
 714            #    bpy.ops.mesh.select_non_manifold(extend=False, use_wire=True, use_boundary=True, use_multi_face=False, use_non_contiguous=False, use_verts=False)
 715            #    for is_comparison, vert in zip(comparison_data, me.vertices):
 716            #        if is_comparison:
 717            #            vert.select = False
 718            #    bpy.ops.mesh.mark_sharp(use_verts=False)
 719            #
 720            #    bpy.ops.object.mode_set(mode='OBJECT')
 721
 722            can_mark_sharp = not compat.IS_LEGACY and bpy.app.version >= (2, 91)
 723
 724            if self.is_remove_doubles:
 725                context.tool_settings.mesh_select_mode = (True, False, False)
 726                bpy.ops.object.mode_set(mode='EDIT')
 727                if not self.is_sharp or not can_mark_sharp:
 728                    bpy.ops.mesh.select_all(action='DESELECT')
 729                    bpy.ops.object.mode_set(mode='OBJECT')
 730                    for is_comparison, vert in zip(comparison_data, me.vertices):
 731                        if is_comparison:
 732                            vert.select = True
 733                    bpy.ops.object.mode_set(mode='EDIT')
 734                else:
 735                    bpy.ops.mesh.select_all(action='SELECT')
 736                
 737                if not can_mark_sharp:
 738                    bpy.ops.mesh.remove_doubles(threshold=0.000001/5 * self.scale)
 739                else:
 740                    bpy.ops.mesh.remove_doubles(threshold=0.000001/5 * self.scale, use_sharp_edge_from_normals=self.is_sharp)
 741                bpy.ops.object.mode_set(mode='OBJECT')
 742            
 743            context.tool_settings.mesh_select_mode = pre_mesh_select_mode
 744
 745            if self.is_seam:
 746                bpy.ops.object.mode_set(mode='EDIT')
 747                bpy.ops.mesh.select_all(action='SELECT')
 748                bpy.ops.uv.select_all(action='SELECT')
 749                bpy.ops.uv.seams_from_islands()
 750                bpy.ops.object.mode_set(mode='OBJECT')
 751            bpy.ops.object.mode_set(mode='EDIT')
 752            bpy.ops.mesh.select_all(action='DESELECT')
 753            bpy.ops.object.mode_set(mode='OBJECT')
 754
 755            if self.is_armature:
 756                mod = ob.modifiers.new("Armature", 'ARMATURE')
 757                mod.object = arm_ob
 758                compat.set_active(context, arm_ob)
 759                bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
 760                compat.set_active(context, ob)
 761        context.window_manager.progress_update(8)
 762
 763        # マテリアル情報のテキスト埋め込み
 764        if self.is_mate_data_text:
 765            for index, data in enumerate(material_data):
 766                txt_name = "Material:" + str(index)
 767                if txt_name in context.blend_data.texts:
 768                    txt = context.blend_data.texts[txt_name]
 769                    txt.clear()
 770                else:
 771                    txt = context.blend_data.texts.new(txt_name)
 772                txt.write(data.to_text())
 773                
 774                # txt.write("1000" + "\n")
 775                # txt.write(data['name1'].lower() + "\n")
 776                # txt.write(data['name1'] + "\n")
 777                # txt.write(data['name2'] + "\n")
 778                # txt.write(data['name3'] + "\n")
 779                # txt.write("\n")
 780                # for tex_data in data['data']:
 781                #     txt.write(tex_data['type'] + "\n")
 782                #     if tex_data['type'] == 'tex':
 783                #         txt.write("\t" + tex_data['name'] + "\n")
 784                #         txt.write("\t" + tex_data['type2'] + "\n")
 785                #         if tex_data['type2'] == 'tex2d':
 786                #             txt.write("\t" + tex_data['name2'] + "\n")
 787                #             txt.write("\t" + tex_data['path'] + "\n")
 788                #             map_list = tex_data['tex_map']
 789                #             tex_map = " ".join([str(map_list[0]), str(map_list[1]), str(map_list[2]), str(map_list[3])])
 790                #             txt.write("\t" + tex_map + "\n")
 791                #     elif tex_data['type'] == 'col':
 792                #         txt.write("\t" + tex_data['name'] + "\n")
 793                #         col = " ".join([str(tex_data['color'][0]), str(tex_data['color'][1]), str(tex_data['color'][2]), str(tex_data['color'][3])])
 794                #         txt.write("\t" + col + "\n")
 795                #     elif tex_data['type'] == 'f':
 796                #         txt.write("\t" + tex_data['name'] + "\n")
 797                #         txt.write("\t" + str(tex_data['float']) + "\n")
 798                # txt.current_line_index = 0
 799        context.window_manager.progress_update(9)
 800
 801        # ボーン情報のテキスト埋め込み
 802        if self.is_bone_data_text:
 803            if "BoneData" in context.blend_data.texts:
 804                txt = context.blend_data.texts["BoneData"]
 805                txt.clear()
 806            else:
 807                txt = context.blend_data.texts.new("BoneData")
 808        for i, data in enumerate(bone_data):
 809            s = ",".join([data['name'], str(data['scl']), ""])
 810            parent_index = data['parent_index']
 811            if -1 < parent_index:
 812                s += bone_data[parent_index]['name'] + ","
 813            else:
 814                s += "None" + ","
 815            s += " ".join([str(data['co'][0]), str(data['co'][1]), str(data['co'][2])]) + ","
 816            s += " ".join([str(data['rot'][0]), str(data['rot'][1]), str(data['rot'][2]), str(data['rot'][3])])
 817            if model_ver >= 2001:
 818                if 'scale' in data:
 819                    s += ",1," + " ".join(map(str, data['scale']))
 820                else:
 821                    s += ",0"
 822
 823            if self.is_bone_data_text:
 824                txt.write(s + "\n")
 825            if self.is_mesh and self.is_bone_data_obj_property:
 826                ob["BoneData:" + str(i)] = s
 827            if self.is_armature and self.is_bone_data_arm_property:
 828                arm["BoneData:" + str(i)] = s
 829        if self.is_bone_data_text:
 830            txt['BaseBone'] = model_name2
 831            txt.current_line_index = 0
 832        context.window_manager.progress_update(10)
 833
 834        # ローカルボーン情報のテキスト埋め込み
 835        if self.is_bone_data_text:
 836            if "LocalBoneData" in context.blend_data.texts:
 837                txt = context.blend_data.texts["LocalBoneData"]
 838                txt.clear()
 839            else:
 840                txt = context.blend_data.texts.new("LocalBoneData")
 841        for i, data in enumerate(local_bone_data):
 842            s = data['name'] + ","
 843
 844            mat_list = list(data['matrix'][0])
 845            mat_list.extend(list(data['matrix'][1]))
 846            mat_list.extend(list(data['matrix'][2]))
 847            mat_list.extend(list(data['matrix'][3]))
 848            for j, f in enumerate(mat_list):
 849                mat_list[j] = str(f)
 850            s += " ".join(mat_list)
 851
 852            if self.is_bone_data_text:
 853                txt.write(s + "\n")
 854            if self.is_mesh and self.is_bone_data_obj_property:
 855                ob["LocalBoneData:" + str(i)] = s
 856            if self.is_armature and self.is_bone_data_arm_property:
 857                arm["LocalBoneData:" + str(i)] = s
 858        if self.is_bone_data_text:
 859            txt['BaseBone'] = model_name2
 860            txt.current_line_index = 0
 861
 862        if self.is_mesh and self.is_bone_data_obj_property:
 863            ob['BaseBone'] = model_name2
 864            if model_ver >= 1000:
 865                ob['ModelVersion'] = model_ver
 866        if self.is_armature and self.is_bone_data_arm_property:
 867            arm['BaseBone'] = model_name2
 868            if model_ver >= 1000:
 869                arm['ModelVersion'] = model_ver
 870        context.window_manager.progress_end()
 871
 872        require_time = time.time() - start_time
 873        filesize = os.path.getsize(self.filepath)
 874        filesize_str = "バイト"
 875        if 1024 * 1024 < filesize:
 876            filesize = filesize / (1024 * 1024.0)
 877            filesize_str = "MB"
 878        elif 1024 < filesize:
 879            filesize = filesize / 1024.0
 880            filesize_str = "KB"
 881        self.report(type={'INFO'}, message=f_tip_("modelのインポートが完了しました ({} {}/ {:.2f} 秒)", filesize, filesize_str, require_time))
 882        
 883        if is_odd_scale_bone:
 884            self.report(type={'WARNING'}, message="Found bone with a scale not equal to 1.")
 885        if is_local_bones_corrupt:
 886            self.report(type={'ERROR'}, message="Found potentially corrupt local bone data, please re-import with \"Use Local Bone Data\" disabled.")
 887        return {'FINISHED'}
 888
 889    def create_mesh(self, context, model_name1, vertex_data, face_data) -> (bpy.types.Object, bpy.types.Mesh):
 890        # メッシュ作成
 891        me = context.blend_data.meshes.new(model_name1)
 892        verts, faces = [], []
 893        for data in vertex_data:
 894            #co = list(data['co'][:])
 895            #co[0] = -co[0]
 896            #co[0] *= self.scale
 897            #co[1] *= self.scale
 898            #co[2] *= self.scale
 899            co = compat.convert_cm_to_bl_space( mathutils.Vector(data['co']) * self.scale )
 900            #co = mathutils.Vector(data['co']) * self.scale
 901            verts.append(co)
 902        context.window_manager.progress_update(2.25)
 903        for data in face_data:
 904            faces.extend(data)
 905        context.window_manager.progress_update(2.5)
 906        me.from_pydata(verts, [], faces)
 907
 908        # オブジェクト化
 909        ob = context.blend_data.objects.new(model_name1, me)
 910        ob.rotation_mode = 'QUATERNION'
 911        compat.link(context.scene, ob)
 912        compat.set_select(ob, True)
 913        compat.set_active(context, ob)
 914        bpy.ops.object.shade_smooth()
 915        context.window_manager.progress_update(2.75)
 916
 917        # Custom Split Normals
 918        #normals_color = me.vertex_colors.new(name=f"Basis_normals", do_init=False) or me.vertex_colors[-1]
 919        #for vert_index, vert in enumerate(vertex_data):
 920        #    no = compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']) ) #mathutils.Vector(vert['normal']) * mathutils.Vector((-1,1,1)) #
 921        #    no.normalize()
 922        #    print(no)
 923        #    for loop_index in vert_loops[vert_index]:
 924        #        #normals[loop_index] = no
 925        #        normals_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
 926        #            *no, #*(no * 0.5 + mathutils.Vector([0.5]*3)),
 927        #            1,
 928        #        )
 929        #me.normals_split_custom_set(normals)
 930        me.normals_split_custom_set_from_vertices(
 931            tuple(
 932                compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']) )
 933                for vert in vertex_data
 934            )
 935        )
 936        me.use_auto_smooth = True
 937
 938        return ob, me
 939
 940    def create_vertex_groups(self, context, ob, vertex_data, local_bone_data):
 941        # 頂点グループ作成
 942        for data in local_bone_data:
 943            ob.vertex_groups.new(name=common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 944        context.window_manager.progress_update(4.333)
 945
 946        for vert_index, data in enumerate(vertex_data):
 947            for weight in data['weights']:
 948                if 0.0 < weight['value']:
 949                    vertex_group = ob.vertex_groups[common.decode_bone_name(weight['name'], self.is_convert_bone_weight_names)]
 950                    vertex_group.add([vert_index], weight['value'], 'REPLACE')
 951        context.window_manager.progress_update(4.666)
 952
 953        if self.is_vertex_group_sort:
 954            bpy.ops.object.vertex_group_sort(sort_type='NAME')
 955
 956        if self.is_remove_empty_vertex_group:
 957            for vg in ob.vertex_groups[:]:
 958                for vert in ob.data.vertices:
 959                    for group in vert.groups:
 960                        if group.group == vg.index:
 961                            if 0.0 < group.weight:
 962                                break
 963                    else: # if for-loop didn't break
 964                        continue
 965                    break
 966                else: # if for-loop didn't break
 967                    ob.vertex_groups.remove(vg)
 968        
 969        ob.vertex_groups.active_index = 0
 970    
 971    def create_uvs(self, context, me, vertex_data, extra_uv_uses):
 972        # UV作成
 973        bm = bmesh.new()
 974        bm.from_mesh(me)
 975        bm.loops.layers.uv.new(f_data_("MainUV"))
 976        for i, used in enumerate(extra_uv_uses):    
 977            if used:
 978                bm.loops.layers.uv.new(f_data_("ExtraUV{num}", num=i))
 979        for face in bm.faces:
 980            for loop in face.loops:
 981                loop[bm.loops.layers.uv[0]].uv = vertex_data[loop.vert.index]['uv']
 982                for extra_uv_index, extra_uv in enumerate(vertex_data[loop.vert.index]['extra_uvs']):
 983                    loop[bm.loops.layers.uv[extra_uv_index+1]].uv = extra_uv
 984        bm.to_mesh(me)
 985        bm.free()
 986
 987    def create_shapekeys(self, context, ob, misc_data):
 988        # モーフ追加
 989        me = ob.data
 990
 991        is_use_attributes = (not compat.IS_LEGACY and bpy.app.version >= (2,92))
 992        is_fast_create = (not compat.IS_LEGACY and bpy.app.version >= (3,2)) 
 993
 994        #if not is_fast_create:
 995        #    bpy.ops.object.mode_set(mode='VERTEX_PAINT')
 996        #    prev_brush_color = context.tool_settings.vertex_paint.brush.color
 997        
 998        vert_loops = dict()
 999        for loop_index, loop in enumerate(me.loops):
1000            vert_loops.setdefault(loop.vertex_index, list()).append(loop_index)
1001        
1002        def fill_color_layer(layer, color):
1003            import numpy as np
1004            color_np = np.array(color, dtype=float)
1005            color_values = np.broadcast_to(color_np, (len(me.loops), len(color_np)))
1006            layer.data.foreach_set('color', color_values.ravel())
1007
1008        def create_normals_color(name):
1009            default_color = (0.5, 0.5, 0.5, 1.0)
1010
1011            #if is_fast_create:
1012            #    bpy.ops.geometry.color_attribute_add(name=name, domain='CORNER', data_type='FLOAT_COLOR', color=default_color)
1013            #    return me.attributes.active
1014            
1015            if is_use_attributes:
1016                normals_color = me.attributes.new(name, 'FLOAT_COLOR', 'CORNER')
1017            else:
1018                normals_color = me.vertex_colors.new(name=name, do_init=False) or me.vertex_colors[-1]
1019
1020            fill_color_layer(normals_color, default_color)
1021            
1022            return normals_color
1023        
1024        def create_unknown_color(data):
1025            unknown_color = None
1026            if len(data['data']) and data['data'][0]['color']:
1027                if is_use_attributes:
1028                    unknown_color = me.attributes.new(f"{data['name']}_unknown", 'FLOAT_COLOR', 'CORNER')
1029                else:
1030                    unknown_color = me.vertex_colors.new(name=f"{data['name']}_unknown", do_init=False) or me.vertex_colors[-1]
1031            return unknown_color
1032
1033        def set_shape_key_data(shape_key, normals_color, unknown_color):
1034            for vert in data['data']:
1035                vert_index = vert['index']
1036                co = compat.convert_cm_to_bl_space( mathutils.Vector(vert['co']) * self.scale )
1037                no = compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']))
1038                shape_key.data[vert_index].co = shape_key.data[vert_index].co + co
1039
1040                write_vertex_colors(vert, no, normals_color, unknown_color)
1041
1042        def write_vertex_colors(vert, no, normals_color, unknown_color):
1043            for loop_index in vert_loops[vert['index']]:
1044                normals_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
1045                    no[0] * 0.5 + 0.5,
1046                    no[1] * 0.5 + 0.5,
1047                    no[2] * 0.5 + 0.5,
1048                    1,
1049                )
1050                if not vert['color']:
1051                    continue
1052                unknown_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
1053                    vert['color'][0] * 0.5 * vert['color'][3] + 0.5,
1054                    vert['color'][1] * 0.5 * vert['color'][3] + 0.5,
1055                    vert['color'][2] * 0.5 * vert['color'][3] + 0.5,
1056                    1,
1057                )
1058
1059        morph_count = -1
1060        for data in misc_data:
1061            if not data['type'] == 'morph':
1062                continue
1063            
1064            morph_count += 1
1065
1066            if morph_count == 0:
1067                bpy.ops.object.shape_key_add(from_mix=False)
1068                me.shape_keys.name = ob.name
1069            shape_key = ob.shape_key_add(name=data['name'], from_mix=False)
1070            
1071            normals_color = create_normals_color(f"{data['name']}_delta_normals")
1072            unknown_color = create_unknown_color(data)
1073            set_shape_key_data(shape_key, normals_color, unknown_color)
1074
1075
1076    def create_mateprop_old(self, context, me, tex_set, mate, mate_idx, data: list):
1077        # create_matepropとの違いは、slot_indexの有無、nodeの接続・配置処理のみ
1078
1079        prefs = common.preferences()
1080        # テクスチャ追加
1081        slot_index = 0
1082        for tex_data in data['data']:
1083            if prefs.mate_unread_same_value:
1084                if tex_data['name'] in tex_set:
1085                    continue
1086                tex_set.add(tex_data['name'])
1087
1088            node_name = tex_data['name']
1089            if tex_data['type'] == 'tex':
1090                path = tex_data['path']
1091                tex_map_data = tex_data['tex_map']
1092                common.create_tex(context, mate, node_name, tex_data['name2'], path, path, tex_map_data, prefs.is_replace_cm3d2_tex, slot_index)
1093
1094            elif tex_data['type'] == 'col':
1095                col = tex_data['color']
1096                common.create_col(context, mate, node_name, col, slot_index)
1097
1098            elif tex_data['type'] == 'f':
1099                f = tex_data['float']
1100                common.create_f(context, mate, node_name, f, slot_index)
1101
1102            slot_index += 1
1103
1104            self.progress(context)
1105
1106    def create_mateprop(self, context, me, tex_set, mate, mate_idx, data: list):
1107        if mate.use_nodes is False:
1108            mate.use_nodes = True
1109
1110        nodes = mate.node_tree.nodes
1111        prefs = common.preferences()
1112
1113        for prop_data in data['data']:
1114            if prefs.mate_unread_same_value:
1115                if prop_data['name'] in tex_set:
1116                    continue
1117                tex_set.add(prop_data['name'])
1118
1119            if prop_data['type'] == 'tex':  # テクスチャ追加
1120                prop_name = prop_data['name']
1121                if prop_data['type2'] == 'tex2d':
1122                    tex_name = prop_data['name2']
1123                    cm3d2path = prop_data['path']
1124                    tex_map = prop_data['tex_map']
1125                    tex = common.create_tex(context, mate, prop_name, tex_name, cm3d2path, cm3d2path, tex_map)
1126
1127                    if prop_data['type2'] == 'tex2d':
1128                        mapping = prop_data['tex_map']
1129                        tex_map = tex.texture_mapping
1130                        tex_map.translation[0] = mapping[0]
1131                        tex_map.translation[1] = mapping[1]
1132                        tex_map.scale[0] = mapping[2]
1133                        tex_map.scale[1] = mapping[3]
1134
1135                        # ファイルの実体を割り当て
1136                        if prefs.is_replace_cm3d2_tex:
1137                            img = tex.image
1138                            # col = mate.node_tree.nodes.new(type='ShaderNodeAttribute')
1139                            # tex.image = bpy.data.images.load("C:\\path\\to\\im.jpg")
1140                            replaced = common.replace_cm3d2_tex(img, self.texpath_dict, reload_path=False)
1141                            if compat.IS_LEGACY and replaced and prop_name == '_MainTex':
1142                                for face in me.polygons:
1143                                    if face.material_index == mate_idx:
1144                                        me.uv_textures.active.data[face.index].image = img
1145                else:
1146                    common.create_tex(context, mate, prop_name)
1147
1148            elif prop_data['type'] == 'col':
1149                col = nodes.new(type='ShaderNodeRGB')
1150                col.name = col.label = prop_data['name']
1151                # val.type = 'RGB'
1152                col.outputs[0].default_value = prop_data['color'][:4]
1153
1154                # mate.node_tree.links.new(bsdf.inputs['xxx'], val.outputs['Color'])
1155                # mate.node_tree.nodes.active = col
1156
1157                # slot = mate.texture_slots.create(tex_index)
1158                # mate.use_textures[tex_index] = False
1159                # slot.diffuse_color_factor = tex_data['color'][3]
1160                # slot.use_rgb_to_intensity = True
1161                # tex = context.blend_data.textures.new(tex_data['name'], 'BLEND')
1162                # slot.texture = tex
1163
1164            elif prop_data['type'] == 'f':
1165                val = nodes.new(type='ShaderNodeValue')
1166                val.name = prop_data['name']
1167                val.label = prop_data['name']
1168                # val.type = 'VALUE'
1169                # mate.node_tree.links.new(bsdf.inputs['xxx'], val.outputs['Value'])
1170
1171                val.outputs[0].default_value = prop_data['float']
1172
1173            self.progress(context)
1174
1175        cm3d2_data.align_nodes(mate)
1176
1177    def progress(self, context):
1178        self.progress_count += self.progress_plus_value
1179        context.window_manager.progress_update(self.progress_count)
1180
1181
1182# メニューを登録する関数
1183def menu_func(self, context):
1184    self.layout.operator(CNV_OT_import_cm3d2_model.bl_idname, icon_value=common.kiss_icon())
@compat.BlRegister()
class CNV_OT_import_cm3d2_model(bpy_types.Operator, bpy_extras.io_utils.ImportHelper):
  20@compat.BlRegister()
  21#@bpy_extras.io_utils.orientation_helper(axis_forward='-Z', axis_up='Y')
  22class CNV_OT_import_cm3d2_model(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
  23    bl_idname = 'import_mesh.import_cm3d2_model'
  24    bl_label = "CM3D2モデル (.model)"
  25    bl_description = "カスタムメイド3D2のmodelファイルを読み込みます"
  26    bl_options = {'REGISTER'}
  27
  28    filepath = bpy.props.StringProperty(subtype='FILE_PATH')
  29    filename_ext = ".model"
  30    filter_glob = bpy.props.StringProperty(default="*.model", options={'HIDDEN'})
  31
  32    scale = bpy.props.FloatProperty(name="倍率", default=5, min=0.1, max=100, soft_min=0.1, soft_max=100, step=100, precision=1, description="インポート時のメッシュ等の拡大率です")
  33
  34    is_mesh = bpy.props.BoolProperty(name="メッシュ生成", default=True, description="ポリゴンを読み込みます、大抵の場合オンでOKです")
  35    is_remove_doubles = bpy.props.BoolProperty(name="重複頂点を結合", default=True, description="UVの切れ目でポリゴンが分かれている仕様なので、インポート時にくっつけます")
  36    is_seam = bpy.props.BoolProperty(name="シームをつける", default=True, description="UVの切れ目にシームをつけます")
  37    is_sharp = bpy.props.BoolProperty(name="Mark Sharp", default=True, description="This will mark removed doubles on your mesh as sharp (or all free edges if not removing doubles).")
  38
  39    is_convert_bone_weight_names = bpy.props.BoolProperty(name="頂点グループ名をBlender用に変換", default=False, description="全ての頂点グループ名をBlenderの左右対称編集で使えるように変換してから読み込みます")
  40    is_vertex_group_sort = bpy.props.BoolProperty(name="頂点グループを名前順ソート", default=True, description="頂点グループを名前順でソートします")
  41    is_remove_empty_vertex_group = bpy.props.BoolProperty(name="割り当てのない頂点グループを削除", default=True, description="全ての頂点に割り当てのない頂点グループを削除します")
  42
  43    reload_tex_cache = bpy.props.BoolProperty(name="テクスチャキャッシュを再構成", default=False, description="texファイルを探す際、キャッシュを再構成します")
  44    is_decorate = bpy.props.BoolProperty(name="種類に合わせてマテリアルを装飾", default=True)
  45    is_mate_data_text = bpy.props.BoolProperty(name="テキストにマテリアル情報埋め込み", default=True, description="シェーダー情報をテキストに埋め込みます")
  46
  47    is_armature = bpy.props.BoolProperty(name="アーマチュア生成", default=True, description="ウェイトを編集する時に役立つアーマチュアを読み込みます")
  48    is_armature_clean = bpy.props.BoolProperty(name="不要なボーンを削除", default=False, description="ウェイトが無いボーンを削除します")
  49    is_custom_bones = bpy.props.BoolProperty(name="Use Custom Bones", default=False, description="Use the currently selected object for custom bone shapes.")
  50    is_use_local_bones = bpy.props.BoolProperty(name="Use Local Bones", default=True, description="Use the Local Bone Data for orientation (more accurate)")
  51
  52    is_bone_data_text = bpy.props.BoolProperty(name="テキスト", default=True, description="ボーン情報をテキストとして読み込みます")
  53    is_bone_data_obj_property = bpy.props.BoolProperty(name="オブジェクトのカスタムプロパティ", default=True, description="メッシュオブジェクトのカスタムプロパティにボーン情報を埋め込みます")
  54    is_bone_data_arm_property = bpy.props.BoolProperty(name="アーマチュアのカスタムプロパティ", default=True, description="アーマチュアデータのカスタムプロパティにボーン情報を埋め込みます")
  55    texpath_dict = None
  56
  57    @classmethod
  58    def poll(cls, context):
  59        return True
  60
  61    def invoke(self, context, event):
  62        prefs = common.preferences()
  63        if prefs.model_default_path:
  64            self.filepath = common.default_cm3d2_dir(prefs.model_default_path, None, "model")
  65        else:
  66            self.filepath = common.default_cm3d2_dir(prefs.model_import_path, None, "model")
  67        self.scale = prefs.scale
  68        self.is_convert_bone_weight_names = prefs.is_convert_bone_weight_names
  69        if compat.IS_LEGACY or bpy.app.version < (2, 91):
  70            self.is_sharp = False
  71        context.window_manager.fileselect_add(self)
  72        return {'RUNNING_MODAL'}
  73
  74    def draw(self, context):
  75        prefs = common.preferences()
  76        self.layout.prop(self, 'scale')
  77
  78        box = self.layout.box()
  79        box.prop(self, 'is_mesh', icon='MESH_DATA')
  80
  81        sub_box = box.box()
  82        sub_box.enabled = self.is_mesh
  83        sub_box.label(text="メッシュ")
  84        sub_box.prop(self, 'is_remove_doubles', icon='STICKY_UVS_VERT')
  85        sub_box.prop(self, 'is_seam' , icon=compat.icon('UV_EDGESEL'))
  86        if not compat.IS_LEGACY and bpy.app.version >= (2, 91):
  87            sub_box.prop(self, 'is_sharp', icon=compat.icon('EDGESEL'))
  88
  89        sub_box = box.box()
  90        sub_box.enabled = self.is_mesh
  91        sub_box.label(text="頂点グループ")
  92        sub_box.prop(self, 'is_vertex_group_sort', icon='SORTALPHA')
  93        sub_box.prop(self, 'is_remove_empty_vertex_group', icon='DISCLOSURE_TRI_DOWN')
  94        sub_box.prop(self, 'is_convert_bone_weight_names', icon='BLENDER')
  95
  96        sub_box = box.box()
  97        sub_box.enabled = self.is_mesh
  98        sub_box.label(text="マテリアル")
  99        sub_box.prop(prefs, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
 100        sub_box.prop(self, 'reload_tex_cache', icon='FILE_REFRESH')
 101        if compat.IS_LEGACY:
 102            sub_box.prop(self, 'is_decorate', icon=compat.icon('SHADING_TEXTURE'))
 103        sub_box.prop(self, 'is_mate_data_text', icon='TEXT')
 104
 105        box = self.layout.box()
 106        box.prop(self, 'is_armature', icon='ARMATURE_DATA')
 107        
 108        sub_box = box.box()
 109        sub_box.label(text="アーマチュア")
 110        sub_box.prop(self , 'is_use_local_bones'          , icon=compat.icon('GROUP_BONE'), text="Use Local Bone Data")
 111        sub_box.prop(self , 'is_armature_clean'           , icon=compat.icon('X'         ))
 112        sub_box.prop(self , 'is_convert_bone_weight_names', icon=compat.icon('BLENDER'   ), text="ボーン名をBlender用に変換")
 113        sub_box.prop(prefs, 'show_bone_in_front'          , icon=compat.icon('HIDE_OFF'  ), text="Show Bones in Front")
 114        row = sub_box.row()
 115        row.prop    (self , 'is_custom_bones'             , icon=compat.icon('BONE_DATA' ), text="Use Selected as Bone Shape"     )
 116        row.enabled = bool(context.object)
 117        
 118        box = self.layout.box()
 119        box.label(text="ボーン情報埋め込み場所")
 120        box.prop(self, 'is_bone_data_text', icon='TEXT')
 121        box.prop(self, 'is_bone_data_obj_property', icon='OBJECT_DATA')
 122        box.prop(self, 'is_bone_data_arm_property', icon='ARMATURE_DATA')
 123
 124    def execute(self, context):
 125        start_time = time.time()
 126
 127        prefs = common.preferences()
 128        prefs.model_import_path = self.filepath
 129        prefs.scale = self.scale
 130        context.window_manager.progress_begin(0, 10)
 131        context.window_manager.progress_update(0)
 132
 133        custom_bone_ob = context.active_object
 134        if not custom_bone_ob:
 135            self.is_custom_bones = False
 136
 137        #global_matrix = bpy_extras.io_utils.axis_conversion(from_forward=self.axis_forward, from_up=self.axis_up).to_4x4()
 138
 139        try:
 140            reader = open(self.filepath, 'rb')
 141        except:
 142            self.report(type={'ERROR'}, message=f_tip_("ファイルを開くのに失敗しました、アクセス不可かファイルが存在しません。file={}", self.filepath))
 143            return {'CANCELLED'}
 144
 145        self.texpath_dict = common.get_texpath_dict(reload=self.reload_tex_cache)
 146
 147        with reader:
 148            # ヘッダー
 149            ext = None
 150            try: # luvoid : utf-8 decoding could possibly throw an error here
 151                ext = common.read_str(reader)
 152            except:
 153                ext = False
 154            if ext != 'CM3D2_MESH':
 155                self.report(type={'ERROR'}, message="これはカスタムメイド3D2のモデルファイルではありません")
 156                return {'CANCELLED'}
 157            model_ver = struct.unpack('<i', reader.read(4))[0]
 158            self.report(type={'INFO'}, message=f_tip_("Model Version = {version}", version=model_ver))
 159            context.window_manager.progress_update(0.1)
 160
 161            try:
 162                # 名前群を取得
 163                model_name1 = common.read_str(reader)
 164                model_name2 = common.read_str(reader)
 165                context.window_manager.progress_update(0.2)
 166
 167                # ボーン情報読み込み
 168                bone_data = []
 169                bone_count = struct.unpack('<i', reader.read(4))[0]
 170                for i in range(bone_count):
 171                    name = common.read_str(reader)
 172                    scl = struct.unpack('<B', reader.read(1))[0]
 173                    bone_data.append({'name': name, 'scl': scl})
 174
 175                for i in range(bone_count):
 176                    parent_index = struct.unpack('<i', reader.read(4))[0]
 177                    parent_name = None
 178                    if parent_index != -1:
 179                        parent_name = bone_data[parent_index]['name']
 180                    bone_data[i]['parent_index'] = parent_index
 181                    bone_data[i]['parent_name'] = parent_name
 182
 183                for i in range(bone_count):
 184                    x, y, z = struct.unpack('<3f', reader.read(3*4))
 185                    bone_data[i]['co'] = mathutils.Vector((x, y, z))
 186
 187                    x, y, z = struct.unpack('<3f', reader.read(3*4))
 188                    w = struct.unpack('<f', reader.read(4))[0]
 189                    bone_data[i]['rot'] = mathutils.Quaternion((w, x, y, z))
 190                    if model_ver >= 2001:
 191                        use_scale = struct.unpack('<B', reader.read(1))[0]
 192                        if use_scale:
 193                            print(bone_data[i]['name'],"has scale data!")
 194                            scale_x, scale_y, scale_z = struct.unpack('<3f', reader.read(3*4))
 195                            bone_data[i]['scale'] = [scale_x, scale_y, scale_z]
 196
 197                context.window_manager.progress_update(0.3)
 198
 199                print(f_("Reading vertex, mesh, and local bone count at 0x{num:02X}", num=reader.tell()))
 200                vertex_count, mesh_count, local_bone_count = struct.unpack('<3i', reader.read(3*4))
 201
 202                # ローカルボーン情報読み込み
 203                local_bone_data = []
 204                for i in range(local_bone_count):
 205                    local_bone_data.append({'name': common.read_str(reader)})
 206
 207                for i in range(local_bone_count):
 208                    row0 = struct.unpack('<4f', reader.read(4 * 4))
 209                    row1 = struct.unpack('<4f', reader.read(4 * 4))
 210                    row2 = struct.unpack('<4f', reader.read(4 * 4))
 211                    row3 = struct.unpack('<4f', reader.read(4 * 4))
 212                    local_bone_data[i]['matrix'] = mathutils.Matrix([row0, row1, row2, row3])
 213                context.window_manager.progress_update(0.4)
 214
 215                # 頂点情報読み込み
 216                vertex_data = []
 217                print(f_("Reading vertex data at 0x{num:02X}", num=reader.tell()))
 218                extra_uv_uses = [False] * 7
 219                if model_ver >= 2102: # CR Edit Mode
 220                    extra_uv_uses = struct.unpack('<7?', reader.read(7))
 221                    print(f_("extra_uv_uses = {boollist}", boollist=extra_uv_uses))
 222                for i in range(vertex_count):
 223                    co = struct.unpack('<3f', reader.read(3 * 4))
 224                    no = struct.unpack('<3f', reader.read(3 * 4))
 225                    uv = struct.unpack('<2f', reader.read(2 * 4))
 226                    extra_uvs = [] # CR Edit
 227                    for i, used in enumerate(extra_uv_uses):
 228                        if used:
 229                            extra_uvs.append(struct.unpack('<2f', reader.read(2 * 4)))
 230                    vertex_data.append({'co': co, 'normal': no, 'uv': uv, 'extra_uvs': extra_uvs})
 231                if self.is_remove_doubles:
 232                    comparison_data = list(hash(repr(v['co']) + " " + repr(v['normal'])) for v in vertex_data)
 233                    comparison_counter = Counter(comparison_data)
 234                    comparison_data = list((comparison_counter[h] > 1) for h in comparison_data)
 235                    del comparison_counter
 236                print(f_("Reading unknown count at 0x{num:02X}", num=reader.tell()))
 237                unknown_count = struct.unpack('<i', reader.read(4))[0]
 238                for i in range(unknown_count):
 239                    struct.unpack('<4f', reader.read(4 * 4))
 240                for i in range(vertex_count):
 241                    indexes = struct.unpack('<4H', reader.read(4 * 2))
 242                    values = struct.unpack('<4f', reader.read(4 * 4))
 243                    vertex_data[i]['weights'] = list({
 244                            'index': index,
 245                            'value': value,
 246                            'name': local_bone_data[index]['name'],
 247                        } for index, value in zip(indexes, values))
 248                context.window_manager.progress_update(0.5)
 249                # 面情報読み込み
 250                face_data = []
 251                for i in range(mesh_count):
 252                    face_count = int(struct.unpack('<i', reader.read(4))[0] / 3)
 253                    datum = [tuple(reversed(struct.unpack('<3H', reader.read(3 * 2)))) for j in range(face_count)]
 254                    face_data.append(datum)
 255                context.window_manager.progress_update(0.6)
 256
 257                # マテリアル情報読み込み
 258                # TODO MaterialHandlerに変更
 259                material_data = []
 260                material_count = struct.unpack('<i', reader.read(4))[0]
 261                for i in range(material_count):
 262                    print(f_("mate count: {num} of {count} @ 0x{pos:02X}", num=i, count=material_count, pos=reader.tell()))
 263                    data = cm3d2_data.MaterialHandler.read(reader, read_header=False, version=model_ver)
 264                    data.name1 = data.name.lower()
 265                    material_data.append(data)
 266                    
 267                    # name1 = common.read_str(reader)
 268                    # name2 = common.read_str(reader)
 269                    # name3 = common.read_str(reader)
 270                    # data_list = []
 271                    # material_data.append({'name1': name1, 'name2': name2, 'name3': name3, 'data': data_list})
 272                    # while True:
 273                    #     data_type = common.read_str(reader)
 274                    #     if data_type == 'tex':
 275                    #         data_item = {'type': data_type}
 276                    #         data_list.append(data_item)
 277                    #         data_item['name'] = common.read_str(reader)
 278                    #         data_item['type2'] = common.read_str(reader)
 279                    #         if data_item['type2'] == 'tex2d':
 280                    #             data_item['name2'] = common.read_str(reader)
 281                    #             data_item['path'] = common.read_str(reader)
 282                    #             data_item['tex_map'] = struct.unpack('<4f', reader.read(4*4))
 283                    #     elif data_type == 'col':
 284                    #         name = common.read_str(reader)
 285                    #         col = struct.unpack('<4f', reader.read(4*4))
 286                    #         data_list.append({'type': data_type, 'name': name, 'color': col})
 287                    #     elif data_type == 'f':
 288                    #         name = common.read_str(reader)
 289                    #         fval = struct.unpack('<f', reader.read(4))[0]
 290                    #         data_list.append({'type': data_type, 'name': name, 'float': fval})
 291                    #     else:
 292                    #         break
 293
 294                context.window_manager.progress_update(0.8)
 295
 296                # その他情報読み込み
 297                misc_data = []
 298                while True:
 299                    #print(f_("Reading data_type at 0x{num:02X}", num=reader.tell()))
 300                    data_type = common.read_str(reader)
 301                    if data_type == 'morph':
 302                        misc_item = {'type': data_type}
 303                        misc_data.append(misc_item)
 304                        misc_item['name'] = common.read_str(reader)
 305                        misc_item['data'] = data_list = []
 306                        morph_vert_count = struct.unpack('<i', reader.read(4))[0]
 307                        morph_extra_uvs = False
 308                        if model_ver >= 2102: # CR Edit Mode
 309                            morph_extra_uvs = struct.unpack('<?', reader.read(1))[0]
 310                            misc_item['uvs'] = []
 311                            print(f_("{morph}.morph_extra_uvs @ 0x{pos:02X} = {bool}", morph=misc_item['name'], bool=morph_extra_uvs, pos=reader.tell()-1))
 312                        for i in range(morph_vert_count):
 313                            index = struct.unpack('<H', reader.read(2))[0]
 314                            co = mathutils.Vector(struct.unpack('<3f', reader.read(3 * 4)))
 315                            normal = struct.unpack('<3f', reader.read(3 * 4))
 316                            extra_uvs = () # CR Edit
 317                            if morph_extra_uvs:
 318                                extra_uvs = struct.unpack('<4f', reader.read(4 * 4))
 319                            data_list.append({'index': index, 'co': co, 'normal': normal, 'color': extra_uvs})
 320                    else:
 321                        break
 322            
 323            except UnicodeDecodeError as e:
 324                msg = [
 325                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()-len(e.object)) + "\n",
 326                    str(e) + "\n",
 327                    *traceback.format_tb(e.__traceback__)
 328                ]
 329                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
 330                print("".join(msg))
 331                return {'CANCELLED'}
 332            
 333            except struct.error as e:
 334                msg = [
 335                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()) + "\n",
 336                    str(e) + "\n",
 337                    *traceback.format_tb(e.__traceback__)
 338                ]
 339                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
 340                print("".join(msg))
 341                return {'CANCELLED'}
 342
 343            except common.CM3D2ImportException as e:
 344                msg = [
 345                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()) + "\n",
 346                    str(e) + "\n",
 347                    *traceback.format_tb(e.__traceback__)
 348                ]
 349                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
 350                print("".join(msg))
 351                return {'CANCELLED'}
 352
 353        context.window_manager.progress_update(1)
 354
 355        try:
 356            bpy.ops.object.mode_set(mode='OBJECT')
 357        except RuntimeError:
 358            pass
 359        bpy.ops.object.select_all(action='DESELECT')
 360
 361        # アーマチュア作成
 362        if self.is_armature:
 363            arm    = bpy.data.armatures.new(model_name1 + ".armature")
 364            arm_ob = bpy.data.objects.new  (model_name1 + ".armature", arm)
 365            compat.link(bpy.context.scene, arm_ob)
 366            compat.set_select(arm_ob, True)
 367            compat.set_active(context, arm_ob)
 368
 369            arm.show_names              = prefs.show_bone_names        
 370            arm.show_axes               = prefs.show_bone_axes         
 371            arm.show_bone_custom_shapes = prefs.show_bone_custom_shapes
 372            arm.show_group_colors       = prefs.show_bone_group_colors
 373            if compat.IS_LEGACY:
 374                arm_ob.show_x_ray = prefs.show_bone_in_front
 375            else:
 376                arm_ob.show_in_front = prefs.show_bone_in_front     
 377
 378            bpy.ops.object.mode_set(mode='EDIT')
 379
 380            is_odd_scale_bone = False
 381
 382            # 基幹ボーンのみ作成
 383            child_data = []
 384            for data in bone_data:
 385                if not data['parent_name']:
 386                    bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 387                    bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
 388                    bone.use_deform = False
 389
 390                    #co.x, co.y, co.z = -co.x, co.z, -co.y
 391                    #rot = compat.mul(rot, mathutils.Quaternion((0, 0, 1), math.radians(90)))
 392                    #rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, rot.z, -rot.y
 393                    
 394                    #co  = data['co' ].copy()
 395                    #rot = data['rot'].copy()
 396                    #co.x, co.y, co.z = -co.x, -co.z, co.y
 397                    #rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, -rot.z, rot.y
 398                    ##rot = compat.mul(rot, mathutils.Quaternion((0, 0, 1), math.radians(-90)))
 399                    #rot = compat.convert_cm_to_bl_bone_rotation(rot)
 400                    #mat = compat.mul(mathutils.Matrix.Translation(co), rot.to_matrix().to_4x4())
 401                    
 402                    co_mat  = mathutils.Matrix.Translation(data['co'].copy() * self.scale)
 403                    rot     = mathutils.Quaternion(data['rot'].copy())
 404                    #rot     = compat.convert_cm_to_bl_bone_rotation(rot)
 405                    rot_mat = rot.to_matrix().to_4x4()
 406                    #rot_mat = compat.convert_cm_to_bl_bone_rotation(rot_mat)
 407                    mat = compat.mul(co_mat, rot_mat)
 408                    mat = compat.convert_cm_to_bl_bone_rotation(mat)
 409                    mat = compat.convert_cm_to_bl_space(mat)
 410                    #mat = compat.mul(mat, compat.CM_TO_BL_LOCAL_BONE_MAT4)
 411                    
 412                    
 413                    #fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
 414                    #fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
 415                    #fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
 416
 417                    #compat.set_bone_matrix(bone, compat.mul4(fix_mat_scale, fix_mat_before, mat, fix_mat_after))
 418                    compat.set_bone_matrix(bone, mat)
 419
 420
 421                    bone["cm3d2_scl_bone"] = 1 if data['scl'] else 0
 422                    if 'scale' in data:
 423                        bone['cm3d2_bone_scale'] = data['scale']
 424                        scale = mathutils.Vector(data['scale'])
 425                        if ( scale - mathutils.Vector((1,1,1)) ).length > 1e-5:
 426                            is_odd_scale_bone = True
 427                            self.report(type={'WARNING'}, message=f_tip_("Bone '{bone_name}' has odd scale '{bone_scale}' (odd by {bone_diff})", bone_name=bone.name, bone_scale=scale, bone_diff=( scale - mathutils.Vector((1,1,1)) ).length))
 428                        scale *= self.scale * 0.01
 429                        scale = compat.convert_cm_to_bl_bone_rotation(scale)
 430                        bone.bbone_x = scale.x
 431                        bone.bbone_z = scale.z
 432                        #look = bone.tail - bone.head
 433                        #look *= scale.y
 434                        #bone.tail = look + bone.head
 435                else:
 436                    child_data.append(data)
 437            context.window_manager.progress_update(1.333)
 438
 439            # 子ボーンを追加していく
 440            while len(child_data):
 441                data = child_data.pop(0)
 442                parent = arm.edit_bones.get(common.decode_bone_name(data['parent_name'], self.is_convert_bone_weight_names))
 443                if parent:
 444                    bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 445                    bone.parent = parent
 446                    bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
 447                    bone.use_deform = False
 448
 449                    #parent_mats = []
 450                    #current_bone = bone
 451                    #while current_bone:
 452                    #    for b in bone_data:
 453                    #        if common.decode_bone_name(b['name'], self.is_convert_bone_weight_names) == current_bone.name:
 454                    #            local_co  = b['co' ].copy()
 455                    #            local_rot = b['rot'].copy()
 456                    #            break
 457                    #
 458                    #    local_co_mat  = mathutils.Matrix.Translation(local_co)
 459                    #    local_rot_mat = local_rot.to_matrix().to_4x4()        
 460                    #    parent_mats.append(compat.mul(local_co_mat, local_rot_mat))
 461                    #
 462                    #    current_bone = current_bone.parent
 463                    #parent_mats.reverse()
 464                    #
 465                    #mat = mathutils.Matrix()
 466                    #for local_mat in parent_mats:
 467                    #    mat = compat.mul(mat, local_mat)
 468                    #mat *= self.scale
 469                    #mat = compat.convert_cm_to_bl_space(mat)
 470                    #mat = compat.convert_cm_to_bl_bone_rotation(mat)
 471                     
 472                    #parent_mat    = compat.mul(
 473                    #    mathutils.Matrix.Translation(parent.matrix.to_translation()),
 474                    #    parent.matrix.to_quaternion().to_matrix().to_4x4()
 475                    #)
 476
 477                    parent_mat = parent.matrix
 478                    
 479                    local_co      = data['co' ].copy() * self.scale
 480                    local_rot     = data['rot'].copy()
 481                    #local_rot     = compat.convert_cm_to_bl_bone_rotation(rot)
 482                    local_co_mat  = mathutils.Matrix.Translation(local_co)
 483                    local_rot_mat = local_rot.to_matrix().to_4x4()
 484                    local_mat     = compat.mul(local_co_mat, local_rot_mat)
 485                    local_mat     = compat.convert_cm_to_bl_bone_space(local_mat)
 486                    #local_mat     = compat.mul(local_mat, mathutils.Matrix.Diagonal((1,1,1)).to_4x4())
 487                    mat = compat.mul(parent_mat, local_mat)
 488                    mat = compat.convert_cm_to_bl_bone_rotation(mat)
 489
 490                    
 491                    #co_mat        = compat.mul( parent.matrix.inverted(), compat.convert_cm_to_bl_local_bone_mat4(local_co_mat) )
 492                    #rot_mat       = compat.mul( local_rot_mat, compat.CM_TO_BL_LOCAL_BONE_MAT4 )
 493                    #mat           = compat.ul(co_mat, rot_mat)
 494                    #local_mat = compat.mul(local_co_mat, local_rot_mat)
 495                    #local_mat *= self.scale
 496                    #mat = compat.mul( parent.matrix, compat.convert_cm_to_bl_local_bone(local_mat) )
 497                    #mat *= self.scale
 498
 499                    #mat *= self.scale
 500
 501                    #co.x, co.y, co.z = -co.y, co.z, co.x
 502                    #rot.w, rot.x, rot.y, rot.z = rot.w, rot.y, -rot.z, -rot.x
 503
 504                    #co  = data['co' ].copy() * self.scale
 505                    #rot = data['rot'].copy()
 506                    #co.x, co.y, co.z = co.z, -co.x, co.y
 507                    ##co = compat.convert_cm_to_bl_local_bone(co)
 508                    ##rot.w, rot.x, rot.y, rot.z = rot.w, -rot.z, rot.x, -rot.y 
 509                    ##rot.w, rot.x, rot.y, rot.z = rot.w, -rot.z, rot.x, -rot.y
 510                    #local_mat = compat.mul(mathutils.Matrix.Translation(co), rot.to_matrix().to_4x4())
 511                    #mat = compat.mul( parent.matrix, local_mat )
 512                    ##mat *= self.scale
 513
 514                    #fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
 515                    #fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
 516                    #fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
 517
 518                    #compat.set_bone_matrix(bone, compat.mul4(fix_mat_scale, fix_mat_before, mat, fix_mat_after))
 519                    compat.set_bone_matrix(bone, mat)
 520                    
 521                    bone['cm3d2_scl_bone'] = 1 if data['scl'] else 0
 522                    if 'scale' in data:
 523                        bone['cm3d2_bone_scale'] = data['scale']
 524                        scale = mathutils.Vector(data['scale'])
 525                        if ( scale - mathutils.Vector((1,1,1)) ).length > 1e-5:
 526                            is_odd_scale_bone = True
 527                            self.report(type={'WARNING'}, message=f_tip_("Bone '{bone_name}' has odd scale '{bone_scale}' (odd by {bone_diff})", bone_name=bone.name, bone_scale=scale, bone_diff=( scale - mathutils.Vector((1,1,1)) ).length))
 528                        scale *= self.scale * 0.01
 529                        bone.bbone_x = scale.x
 530                        bone.bbone_z = scale.z
 531                        #bone.bbone_segments = scale.y
 532                        #look = bone.tail - bone.head
 533                        #look *= scale.y
 534                        #bone.tail = look + bone.head
 535                else:
 536                    child_data.append(data)
 537            context.window_manager.progress_update(1.666)
 538            
 539            # Configure bones in local bone data
 540            is_local_bones_corrupt = False
 541            base_bone = arm.edit_bones.get(common.decode_bone_name(model_name2, self.is_convert_bone_weight_names))
 542            base_bone_offset = base_bone.matrix.copy()
 543            base_bone_offset = compat.mul(mathutils.Matrix.Scale(-1, 4, (1, 0, 0)), base_bone_offset)
 544            base_bone_offset = compat.convert_bl_to_cm_bone_rotation(base_bone_offset)
 545            print(base_bone_offset)
 546            print(f"base_bone_offset @ I =\n{base_bone_offset @ mathutils.Matrix.Identity(4)}")
 547            #base_bone_offset = mathutils.Matrix.Identity(4) # compat.mul(base_bone_mat.inverted(), base_bone_mat)
 548
 549            def setup_local_bone(bone, mat, isRoot=False):
 550                pos = compat.transform_inverse(mat.transposed()).translation
 551                mat.row[3] = (0.0, 0.0, 0.0, 1.0)
 552                mat.translation = pos
 553                mat.translation *= self.scale
 554                offset_mat = mat.copy()
 555                if not common.is_descendant_of(bone, base_bone):
 556                    mat = compat.mul(base_bone_offset, mat)
 557                    #mat.translation = mat.translation + base_bone_offset.translation
 558                mat = compat.convert_cm_to_bl_bone_rotation(mat)
 559                mat = compat.mul(mathutils.Matrix.Scale(-1, 4, (1, 0, 0)), mat)
 560                
 561            
 562                # The matrices from the local bone data are more precise rotations, but make sure they aren't corrupted
 563                old_pos, old_rot, old_scale = bone.matrix.decompose()
 564                compat.set_bone_matrix(bone, mat)
 565                new_pos, new_rot, new_scale = bone.matrix.decompose()
 566                dif_pos = (new_pos-old_pos).length/self.scale
 567                dif_rot = old_rot.rotation_difference(new_rot)
 568                if dif_pos > 0.1 or dif_rot.w < .9:
 569                    print(dif_pos,  dif_rot)
 570                    is_local_bones_corrupt = True
 571                    #self.report(type={'WARNING'}, message="Found potentially corrupt local bone data, please re-import with \"Use Local Bone Data\" disabled.")
 572                return mat
 573
 574            for data in local_bone_data:
 575                if self.is_use_local_bones and data['name'] == model_name2:
 576                    bone = arm.edit_bones.get(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 577                    mat = mathutils.Matrix(data['matrix'])
 578                    print("Found base bone in local bone data!")
 579                    #base_bone_offset = compat.mul(compat.transform_inverse(base_bone_offset), setup_local_bone(bone, mat, isRoot=True))
 580
 581
 582            for data in local_bone_data:
 583                bone = arm.edit_bones.get(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 584                bone.use_deform = True
 585                if self.is_use_local_bones and not data['name'] == model_name2:
 586                    mat = mathutils.Matrix(data['matrix'])
 587                    setup_local_bone(bone, mat)
 588            
 589            
 590            def distOnRay(pos0, pos1, point):
 591                w = point - pos0
 592                d = (pos1 - pos0).normalized()
 593                return w.dot(d) / d.dot(d)
 594            
 595            # ボーン整頓
 596            for bone in arm.edit_bones:
 597                if len(bone.children) == 0:
 598                    if bone.parent:
 599                        bone.length = bone.parent.length * 0.5
 600                    else:
 601                        bone.length = 0.2 * self.scale
 602                elif len(bone.children) == 1:
 603                    co = bone.children[0].head - bone.head
 604                    bone.length = co.length
 605                elif len(bone.children) >= 2:
 606                    if bone.parent:
 607                        max_len = 0.0
 608                        for child_bone in bone.children:
 609                            if "Pelvis" in bone.name:
 610                                dist = (child_bone.head - bone.head).length
 611                            else:
 612                                dist = distOnRay(bone.head, bone.tail, child_bone.head)
 613                            if dist > max_len:
 614                                max_len = dist
 615                        bone.length = max_len
 616                    else:
 617                        bone.length = 0.2 * self.scale
 618            for bone in arm.edit_bones:
 619                if len(bone.children) == 0:
 620                    if bone.parent:
 621                        bone.length = bone.parent.length * 0.5
 622            
 623            # Make sure no bones are length 0, otherwise blender deletes them
 624            for bone in arm.edit_bones:
 625                min_length = 0.0001
 626                if bone.length < min_length:
 627                    bone.length = min_length
 628            
 629            # 一部ボーン削除
 630            if self.is_armature_clean:
 631                for bone in arm.edit_bones:
 632                    for b in local_bone_data:
 633                        name = common.decode_bone_name(b['name'], self.is_convert_bone_weight_names)
 634                        if bone.name == name:
 635                            break
 636                    else:
 637                        arm.edit_bones.remove(bone)
 638
 639            arm.layers[16] = True
 640            compat.set_display_type(arm, prefs.bone_display_type)
 641            bpy.ops.armature.select_all(action='DESELECT')
 642            bpy.ops.object.mode_set(mode='OBJECT')
 643            if self.is_custom_bones:
 644                print("Set custom bones")
 645                for pose_bone in arm_ob.pose.bones:
 646                    pose_bone.custom_shape = custom_bone_ob
 647        context.window_manager.progress_update(2)
 648
 649        if self.is_mesh:
 650            ob, me = self.create_mesh(context, model_name1, vertex_data, face_data)
 651            # オブジェクト変形
 652            CNV_OT_align_to_cm3d2_base_bone.from_bone_data(ob, bone_data, local_bone_data, base_bone_name=model_name2, scale=self.scale)
 653            context.window_manager.progress_update(3)
 654            
 655            self.create_uvs(context, me, vertex_data, extra_uv_uses)
 656            context.window_manager.progress_update(4)
 657
 658            self.create_vertex_groups(context, ob, vertex_data, local_bone_data)
 659            context.window_manager.progress_update(5)
 660
 661            self.create_shapekeys(context, ob, misc_data)
 662            context.window_manager.progress_update(6)
 663
 664            # マテリアル追加
 665            progress_count_total = 0.0
 666            for data in material_data:
 667                progress_count_total += 1 #len(data['data'])
 668            self.progress_plus_value = 1.0 / (progress_count_total if progress_count_total > 0.0 else 1.0)
 669            self.progress_count = 6.0
 670
 671            face_seek = 0
 672            mates_set = set()
 673            override = context.copy()
 674            override['object'] = ob
 675            prefs = common.preferences()
 676            for index, data in enumerate(material_data):
 677                print(f_("material count: {num} of {count}", num=index, count=material_count))
 678                if prefs.mate_unread_same_value and data.name in mates_set:
 679                    continue
 680                mates_set.add(data.name)
 681                #common.preferences().mate_unread_same_value
 682                bpy.ops.object.material_slot_add(override)
 683                mate = context.blend_data.materials.new(data.name)#['name1'])
 684                #mate['shader1'] = data['name2']
 685                #mate['shader2'] = data['name3']
 686
 687                ob.material_slots[-1].material = mate
 688                # 面にマテリアル割り当て
 689                for i in range(face_seek, face_seek + len(face_data[index])):
 690                    me.polygons[i].material_index = index
 691                face_seek += len(face_data[index])
 692
 693                # テクスチャ追加
 694                if compat.IS_LEGACY:
 695                    #self.create_mateprop_old(context, me, texes_set, mate, index, data)
 696                    cm3d2_data.MaterialHandler.apply_to_old(override, mate, data)
 697                    common.decorate_material(mate, self.is_decorate, me, index)
 698                else:
 699                    #self.create_mateprop(context, me, texes_set, mate, index, data)
 700                    cm3d2_data.MaterialHandler.apply_to(override, mate, data)
 701                    common.decorate_material(mate, self.is_decorate, me, index)
 702                common.setup_material(mate)
 703
 704            ob.active_material_index = 0
 705            context.window_manager.progress_update(7)
 706
 707            # メッシュ整頓
 708            pre_mesh_select_mode = context.tool_settings.mesh_select_mode[:]
 709            
 710            # Too buggy on versions before 2.91 so just disable it outright
 711            #if self.is_sharp and (compat.IS_LEGACY or bpy.app.version < (2, 91)):
 712            #    context.tool_settings.mesh_select_mode = (False, True, False)
 713            #    bpy.ops.object.mode_set(mode='EDIT')
 714            #    
 715            #    bpy.ops.mesh.select_non_manifold(extend=False, use_wire=True, use_boundary=True, use_multi_face=False, use_non_contiguous=False, use_verts=False)
 716            #    for is_comparison, vert in zip(comparison_data, me.vertices):
 717            #        if is_comparison:
 718            #            vert.select = False
 719            #    bpy.ops.mesh.mark_sharp(use_verts=False)
 720            #
 721            #    bpy.ops.object.mode_set(mode='OBJECT')
 722
 723            can_mark_sharp = not compat.IS_LEGACY and bpy.app.version >= (2, 91)
 724
 725            if self.is_remove_doubles:
 726                context.tool_settings.mesh_select_mode = (True, False, False)
 727                bpy.ops.object.mode_set(mode='EDIT')
 728                if not self.is_sharp or not can_mark_sharp:
 729                    bpy.ops.mesh.select_all(action='DESELECT')
 730                    bpy.ops.object.mode_set(mode='OBJECT')
 731                    for is_comparison, vert in zip(comparison_data, me.vertices):
 732                        if is_comparison:
 733                            vert.select = True
 734                    bpy.ops.object.mode_set(mode='EDIT')
 735                else:
 736                    bpy.ops.mesh.select_all(action='SELECT')
 737                
 738                if not can_mark_sharp:
 739                    bpy.ops.mesh.remove_doubles(threshold=0.000001/5 * self.scale)
 740                else:
 741                    bpy.ops.mesh.remove_doubles(threshold=0.000001/5 * self.scale, use_sharp_edge_from_normals=self.is_sharp)
 742                bpy.ops.object.mode_set(mode='OBJECT')
 743            
 744            context.tool_settings.mesh_select_mode = pre_mesh_select_mode
 745
 746            if self.is_seam:
 747                bpy.ops.object.mode_set(mode='EDIT')
 748                bpy.ops.mesh.select_all(action='SELECT')
 749                bpy.ops.uv.select_all(action='SELECT')
 750                bpy.ops.uv.seams_from_islands()
 751                bpy.ops.object.mode_set(mode='OBJECT')
 752            bpy.ops.object.mode_set(mode='EDIT')
 753            bpy.ops.mesh.select_all(action='DESELECT')
 754            bpy.ops.object.mode_set(mode='OBJECT')
 755
 756            if self.is_armature:
 757                mod = ob.modifiers.new("Armature", 'ARMATURE')
 758                mod.object = arm_ob
 759                compat.set_active(context, arm_ob)
 760                bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
 761                compat.set_active(context, ob)
 762        context.window_manager.progress_update(8)
 763
 764        # マテリアル情報のテキスト埋め込み
 765        if self.is_mate_data_text:
 766            for index, data in enumerate(material_data):
 767                txt_name = "Material:" + str(index)
 768                if txt_name in context.blend_data.texts:
 769                    txt = context.blend_data.texts[txt_name]
 770                    txt.clear()
 771                else:
 772                    txt = context.blend_data.texts.new(txt_name)
 773                txt.write(data.to_text())
 774                
 775                # txt.write("1000" + "\n")
 776                # txt.write(data['name1'].lower() + "\n")
 777                # txt.write(data['name1'] + "\n")
 778                # txt.write(data['name2'] + "\n")
 779                # txt.write(data['name3'] + "\n")
 780                # txt.write("\n")
 781                # for tex_data in data['data']:
 782                #     txt.write(tex_data['type'] + "\n")
 783                #     if tex_data['type'] == 'tex':
 784                #         txt.write("\t" + tex_data['name'] + "\n")
 785                #         txt.write("\t" + tex_data['type2'] + "\n")
 786                #         if tex_data['type2'] == 'tex2d':
 787                #             txt.write("\t" + tex_data['name2'] + "\n")
 788                #             txt.write("\t" + tex_data['path'] + "\n")
 789                #             map_list = tex_data['tex_map']
 790                #             tex_map = " ".join([str(map_list[0]), str(map_list[1]), str(map_list[2]), str(map_list[3])])
 791                #             txt.write("\t" + tex_map + "\n")
 792                #     elif tex_data['type'] == 'col':
 793                #         txt.write("\t" + tex_data['name'] + "\n")
 794                #         col = " ".join([str(tex_data['color'][0]), str(tex_data['color'][1]), str(tex_data['color'][2]), str(tex_data['color'][3])])
 795                #         txt.write("\t" + col + "\n")
 796                #     elif tex_data['type'] == 'f':
 797                #         txt.write("\t" + tex_data['name'] + "\n")
 798                #         txt.write("\t" + str(tex_data['float']) + "\n")
 799                # txt.current_line_index = 0
 800        context.window_manager.progress_update(9)
 801
 802        # ボーン情報のテキスト埋め込み
 803        if self.is_bone_data_text:
 804            if "BoneData" in context.blend_data.texts:
 805                txt = context.blend_data.texts["BoneData"]
 806                txt.clear()
 807            else:
 808                txt = context.blend_data.texts.new("BoneData")
 809        for i, data in enumerate(bone_data):
 810            s = ",".join([data['name'], str(data['scl']), ""])
 811            parent_index = data['parent_index']
 812            if -1 < parent_index:
 813                s += bone_data[parent_index]['name'] + ","
 814            else:
 815                s += "None" + ","
 816            s += " ".join([str(data['co'][0]), str(data['co'][1]), str(data['co'][2])]) + ","
 817            s += " ".join([str(data['rot'][0]), str(data['rot'][1]), str(data['rot'][2]), str(data['rot'][3])])
 818            if model_ver >= 2001:
 819                if 'scale' in data:
 820                    s += ",1," + " ".join(map(str, data['scale']))
 821                else:
 822                    s += ",0"
 823
 824            if self.is_bone_data_text:
 825                txt.write(s + "\n")
 826            if self.is_mesh and self.is_bone_data_obj_property:
 827                ob["BoneData:" + str(i)] = s
 828            if self.is_armature and self.is_bone_data_arm_property:
 829                arm["BoneData:" + str(i)] = s
 830        if self.is_bone_data_text:
 831            txt['BaseBone'] = model_name2
 832            txt.current_line_index = 0
 833        context.window_manager.progress_update(10)
 834
 835        # ローカルボーン情報のテキスト埋め込み
 836        if self.is_bone_data_text:
 837            if "LocalBoneData" in context.blend_data.texts:
 838                txt = context.blend_data.texts["LocalBoneData"]
 839                txt.clear()
 840            else:
 841                txt = context.blend_data.texts.new("LocalBoneData")
 842        for i, data in enumerate(local_bone_data):
 843            s = data['name'] + ","
 844
 845            mat_list = list(data['matrix'][0])
 846            mat_list.extend(list(data['matrix'][1]))
 847            mat_list.extend(list(data['matrix'][2]))
 848            mat_list.extend(list(data['matrix'][3]))
 849            for j, f in enumerate(mat_list):
 850                mat_list[j] = str(f)
 851            s += " ".join(mat_list)
 852
 853            if self.is_bone_data_text:
 854                txt.write(s + "\n")
 855            if self.is_mesh and self.is_bone_data_obj_property:
 856                ob["LocalBoneData:" + str(i)] = s
 857            if self.is_armature and self.is_bone_data_arm_property:
 858                arm["LocalBoneData:" + str(i)] = s
 859        if self.is_bone_data_text:
 860            txt['BaseBone'] = model_name2
 861            txt.current_line_index = 0
 862
 863        if self.is_mesh and self.is_bone_data_obj_property:
 864            ob['BaseBone'] = model_name2
 865            if model_ver >= 1000:
 866                ob['ModelVersion'] = model_ver
 867        if self.is_armature and self.is_bone_data_arm_property:
 868            arm['BaseBone'] = model_name2
 869            if model_ver >= 1000:
 870                arm['ModelVersion'] = model_ver
 871        context.window_manager.progress_end()
 872
 873        require_time = time.time() - start_time
 874        filesize = os.path.getsize(self.filepath)
 875        filesize_str = "バイト"
 876        if 1024 * 1024 < filesize:
 877            filesize = filesize / (1024 * 1024.0)
 878            filesize_str = "MB"
 879        elif 1024 < filesize:
 880            filesize = filesize / 1024.0
 881            filesize_str = "KB"
 882        self.report(type={'INFO'}, message=f_tip_("modelのインポートが完了しました ({} {}/ {:.2f} 秒)", filesize, filesize_str, require_time))
 883        
 884        if is_odd_scale_bone:
 885            self.report(type={'WARNING'}, message="Found bone with a scale not equal to 1.")
 886        if is_local_bones_corrupt:
 887            self.report(type={'ERROR'}, message="Found potentially corrupt local bone data, please re-import with \"Use Local Bone Data\" disabled.")
 888        return {'FINISHED'}
 889
 890    def create_mesh(self, context, model_name1, vertex_data, face_data) -> (bpy.types.Object, bpy.types.Mesh):
 891        # メッシュ作成
 892        me = context.blend_data.meshes.new(model_name1)
 893        verts, faces = [], []
 894        for data in vertex_data:
 895            #co = list(data['co'][:])
 896            #co[0] = -co[0]
 897            #co[0] *= self.scale
 898            #co[1] *= self.scale
 899            #co[2] *= self.scale
 900            co = compat.convert_cm_to_bl_space( mathutils.Vector(data['co']) * self.scale )
 901            #co = mathutils.Vector(data['co']) * self.scale
 902            verts.append(co)
 903        context.window_manager.progress_update(2.25)
 904        for data in face_data:
 905            faces.extend(data)
 906        context.window_manager.progress_update(2.5)
 907        me.from_pydata(verts, [], faces)
 908
 909        # オブジェクト化
 910        ob = context.blend_data.objects.new(model_name1, me)
 911        ob.rotation_mode = 'QUATERNION'
 912        compat.link(context.scene, ob)
 913        compat.set_select(ob, True)
 914        compat.set_active(context, ob)
 915        bpy.ops.object.shade_smooth()
 916        context.window_manager.progress_update(2.75)
 917
 918        # Custom Split Normals
 919        #normals_color = me.vertex_colors.new(name=f"Basis_normals", do_init=False) or me.vertex_colors[-1]
 920        #for vert_index, vert in enumerate(vertex_data):
 921        #    no = compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']) ) #mathutils.Vector(vert['normal']) * mathutils.Vector((-1,1,1)) #
 922        #    no.normalize()
 923        #    print(no)
 924        #    for loop_index in vert_loops[vert_index]:
 925        #        #normals[loop_index] = no
 926        #        normals_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
 927        #            *no, #*(no * 0.5 + mathutils.Vector([0.5]*3)),
 928        #            1,
 929        #        )
 930        #me.normals_split_custom_set(normals)
 931        me.normals_split_custom_set_from_vertices(
 932            tuple(
 933                compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']) )
 934                for vert in vertex_data
 935            )
 936        )
 937        me.use_auto_smooth = True
 938
 939        return ob, me
 940
 941    def create_vertex_groups(self, context, ob, vertex_data, local_bone_data):
 942        # 頂点グループ作成
 943        for data in local_bone_data:
 944            ob.vertex_groups.new(name=common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
 945        context.window_manager.progress_update(4.333)
 946
 947        for vert_index, data in enumerate(vertex_data):
 948            for weight in data['weights']:
 949                if 0.0 < weight['value']:
 950                    vertex_group = ob.vertex_groups[common.decode_bone_name(weight['name'], self.is_convert_bone_weight_names)]
 951                    vertex_group.add([vert_index], weight['value'], 'REPLACE')
 952        context.window_manager.progress_update(4.666)
 953
 954        if self.is_vertex_group_sort:
 955            bpy.ops.object.vertex_group_sort(sort_type='NAME')
 956
 957        if self.is_remove_empty_vertex_group:
 958            for vg in ob.vertex_groups[:]:
 959                for vert in ob.data.vertices:
 960                    for group in vert.groups:
 961                        if group.group == vg.index:
 962                            if 0.0 < group.weight:
 963                                break
 964                    else: # if for-loop didn't break
 965                        continue
 966                    break
 967                else: # if for-loop didn't break
 968                    ob.vertex_groups.remove(vg)
 969        
 970        ob.vertex_groups.active_index = 0
 971    
 972    def create_uvs(self, context, me, vertex_data, extra_uv_uses):
 973        # UV作成
 974        bm = bmesh.new()
 975        bm.from_mesh(me)
 976        bm.loops.layers.uv.new(f_data_("MainUV"))
 977        for i, used in enumerate(extra_uv_uses):    
 978            if used:
 979                bm.loops.layers.uv.new(f_data_("ExtraUV{num}", num=i))
 980        for face in bm.faces:
 981            for loop in face.loops:
 982                loop[bm.loops.layers.uv[0]].uv = vertex_data[loop.vert.index]['uv']
 983                for extra_uv_index, extra_uv in enumerate(vertex_data[loop.vert.index]['extra_uvs']):
 984                    loop[bm.loops.layers.uv[extra_uv_index+1]].uv = extra_uv
 985        bm.to_mesh(me)
 986        bm.free()
 987
 988    def create_shapekeys(self, context, ob, misc_data):
 989        # モーフ追加
 990        me = ob.data
 991
 992        is_use_attributes = (not compat.IS_LEGACY and bpy.app.version >= (2,92))
 993        is_fast_create = (not compat.IS_LEGACY and bpy.app.version >= (3,2)) 
 994
 995        #if not is_fast_create:
 996        #    bpy.ops.object.mode_set(mode='VERTEX_PAINT')
 997        #    prev_brush_color = context.tool_settings.vertex_paint.brush.color
 998        
 999        vert_loops = dict()
1000        for loop_index, loop in enumerate(me.loops):
1001            vert_loops.setdefault(loop.vertex_index, list()).append(loop_index)
1002        
1003        def fill_color_layer(layer, color):
1004            import numpy as np
1005            color_np = np.array(color, dtype=float)
1006            color_values = np.broadcast_to(color_np, (len(me.loops), len(color_np)))
1007            layer.data.foreach_set('color', color_values.ravel())
1008
1009        def create_normals_color(name):
1010            default_color = (0.5, 0.5, 0.5, 1.0)
1011
1012            #if is_fast_create:
1013            #    bpy.ops.geometry.color_attribute_add(name=name, domain='CORNER', data_type='FLOAT_COLOR', color=default_color)
1014            #    return me.attributes.active
1015            
1016            if is_use_attributes:
1017                normals_color = me.attributes.new(name, 'FLOAT_COLOR', 'CORNER')
1018            else:
1019                normals_color = me.vertex_colors.new(name=name, do_init=False) or me.vertex_colors[-1]
1020
1021            fill_color_layer(normals_color, default_color)
1022            
1023            return normals_color
1024        
1025        def create_unknown_color(data):
1026            unknown_color = None
1027            if len(data['data']) and data['data'][0]['color']:
1028                if is_use_attributes:
1029                    unknown_color = me.attributes.new(f"{data['name']}_unknown", 'FLOAT_COLOR', 'CORNER')
1030                else:
1031                    unknown_color = me.vertex_colors.new(name=f"{data['name']}_unknown", do_init=False) or me.vertex_colors[-1]
1032            return unknown_color
1033
1034        def set_shape_key_data(shape_key, normals_color, unknown_color):
1035            for vert in data['data']:
1036                vert_index = vert['index']
1037                co = compat.convert_cm_to_bl_space( mathutils.Vector(vert['co']) * self.scale )
1038                no = compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']))
1039                shape_key.data[vert_index].co = shape_key.data[vert_index].co + co
1040
1041                write_vertex_colors(vert, no, normals_color, unknown_color)
1042
1043        def write_vertex_colors(vert, no, normals_color, unknown_color):
1044            for loop_index in vert_loops[vert['index']]:
1045                normals_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
1046                    no[0] * 0.5 + 0.5,
1047                    no[1] * 0.5 + 0.5,
1048                    no[2] * 0.5 + 0.5,
1049                    1,
1050                )
1051                if not vert['color']:
1052                    continue
1053                unknown_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
1054                    vert['color'][0] * 0.5 * vert['color'][3] + 0.5,
1055                    vert['color'][1] * 0.5 * vert['color'][3] + 0.5,
1056                    vert['color'][2] * 0.5 * vert['color'][3] + 0.5,
1057                    1,
1058                )
1059
1060        morph_count = -1
1061        for data in misc_data:
1062            if not data['type'] == 'morph':
1063                continue
1064            
1065            morph_count += 1
1066
1067            if morph_count == 0:
1068                bpy.ops.object.shape_key_add(from_mix=False)
1069                me.shape_keys.name = ob.name
1070            shape_key = ob.shape_key_add(name=data['name'], from_mix=False)
1071            
1072            normals_color = create_normals_color(f"{data['name']}_delta_normals")
1073            unknown_color = create_unknown_color(data)
1074            set_shape_key_data(shape_key, normals_color, unknown_color)
1075
1076
1077    def create_mateprop_old(self, context, me, tex_set, mate, mate_idx, data: list):
1078        # create_matepropとの違いは、slot_indexの有無、nodeの接続・配置処理のみ
1079
1080        prefs = common.preferences()
1081        # テクスチャ追加
1082        slot_index = 0
1083        for tex_data in data['data']:
1084            if prefs.mate_unread_same_value:
1085                if tex_data['name'] in tex_set:
1086                    continue
1087                tex_set.add(tex_data['name'])
1088
1089            node_name = tex_data['name']
1090            if tex_data['type'] == 'tex':
1091                path = tex_data['path']
1092                tex_map_data = tex_data['tex_map']
1093                common.create_tex(context, mate, node_name, tex_data['name2'], path, path, tex_map_data, prefs.is_replace_cm3d2_tex, slot_index)
1094
1095            elif tex_data['type'] == 'col':
1096                col = tex_data['color']
1097                common.create_col(context, mate, node_name, col, slot_index)
1098
1099            elif tex_data['type'] == 'f':
1100                f = tex_data['float']
1101                common.create_f(context, mate, node_name, f, slot_index)
1102
1103            slot_index += 1
1104
1105            self.progress(context)
1106
1107    def create_mateprop(self, context, me, tex_set, mate, mate_idx, data: list):
1108        if mate.use_nodes is False:
1109            mate.use_nodes = True
1110
1111        nodes = mate.node_tree.nodes
1112        prefs = common.preferences()
1113
1114        for prop_data in data['data']:
1115            if prefs.mate_unread_same_value:
1116                if prop_data['name'] in tex_set:
1117                    continue
1118                tex_set.add(prop_data['name'])
1119
1120            if prop_data['type'] == 'tex':  # テクスチャ追加
1121                prop_name = prop_data['name']
1122                if prop_data['type2'] == 'tex2d':
1123                    tex_name = prop_data['name2']
1124                    cm3d2path = prop_data['path']
1125                    tex_map = prop_data['tex_map']
1126                    tex = common.create_tex(context, mate, prop_name, tex_name, cm3d2path, cm3d2path, tex_map)
1127
1128                    if prop_data['type2'] == 'tex2d':
1129                        mapping = prop_data['tex_map']
1130                        tex_map = tex.texture_mapping
1131                        tex_map.translation[0] = mapping[0]
1132                        tex_map.translation[1] = mapping[1]
1133                        tex_map.scale[0] = mapping[2]
1134                        tex_map.scale[1] = mapping[3]
1135
1136                        # ファイルの実体を割り当て
1137                        if prefs.is_replace_cm3d2_tex:
1138                            img = tex.image
1139                            # col = mate.node_tree.nodes.new(type='ShaderNodeAttribute')
1140                            # tex.image = bpy.data.images.load("C:\\path\\to\\im.jpg")
1141                            replaced = common.replace_cm3d2_tex(img, self.texpath_dict, reload_path=False)
1142                            if compat.IS_LEGACY and replaced and prop_name == '_MainTex':
1143                                for face in me.polygons:
1144                                    if face.material_index == mate_idx:
1145                                        me.uv_textures.active.data[face.index].image = img
1146                else:
1147                    common.create_tex(context, mate, prop_name)
1148
1149            elif prop_data['type'] == 'col':
1150                col = nodes.new(type='ShaderNodeRGB')
1151                col.name = col.label = prop_data['name']
1152                # val.type = 'RGB'
1153                col.outputs[0].default_value = prop_data['color'][:4]
1154
1155                # mate.node_tree.links.new(bsdf.inputs['xxx'], val.outputs['Color'])
1156                # mate.node_tree.nodes.active = col
1157
1158                # slot = mate.texture_slots.create(tex_index)
1159                # mate.use_textures[tex_index] = False
1160                # slot.diffuse_color_factor = tex_data['color'][3]
1161                # slot.use_rgb_to_intensity = True
1162                # tex = context.blend_data.textures.new(tex_data['name'], 'BLEND')
1163                # slot.texture = tex
1164
1165            elif prop_data['type'] == 'f':
1166                val = nodes.new(type='ShaderNodeValue')
1167                val.name = prop_data['name']
1168                val.label = prop_data['name']
1169                # val.type = 'VALUE'
1170                # mate.node_tree.links.new(bsdf.inputs['xxx'], val.outputs['Value'])
1171
1172                val.outputs[0].default_value = prop_data['float']
1173
1174            self.progress(context)
1175
1176        cm3d2_data.align_nodes(mate)
1177
1178    def progress(self, context):
1179        self.progress_count += self.progress_plus_value
1180        context.window_manager.progress_update(self.progress_count)
bl_idname = 'import_mesh.import_cm3d2_model'
bl_label = 'CM3D2モデル (.model)'
bl_description = 'カスタムメイド3D2のmodelファイルを読み込みます'
bl_options = {'REGISTER'}
filepath: <_PropertyDeferred, <built-in function StringProperty>, {'subtype': 'FILE_PATH', 'attr': 'filepath'}> = <_PropertyDeferred, <built-in function StringProperty>, {'subtype': 'FILE_PATH', 'attr': 'filepath'}>
filename_ext = '.model'
filter_glob: <_PropertyDeferred, <built-in function StringProperty>, {'default': '*.model', 'options': {'HIDDEN'}, 'attr': 'filter_glob'}> = <_PropertyDeferred, <built-in function StringProperty>, {'default': '*.model', 'options': {'HIDDEN'}, 'attr': 'filter_glob'}>
scale: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'default': 5, 'min': 0.1, 'max': 100, 'soft_min': 0.1, 'soft_max': 100, 'step': 100, 'precision': 1, 'description': 'インポート時のメッシュ等の拡大率です', 'attr': 'scale'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'default': 5, 'min': 0.1, 'max': 100, 'soft_min': 0.1, 'soft_max': 100, 'step': 100, 'precision': 1, 'description': 'インポート時のメッシュ等の拡大率です', 'attr': 'scale'}>
is_mesh: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'メッシュ生成', 'default': True, 'description': 'ポリゴンを読み込みます、大抵の場合オンでOKです', 'attr': 'is_mesh'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'メッシュ生成', 'default': True, 'description': 'ポリゴンを読み込みます、大抵の場合オンでOKです', 'attr': 'is_mesh'}>
is_remove_doubles: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '重複頂点を結合', 'default': True, 'description': 'UVの切れ目でポリゴンが分かれている仕様なので、インポート時にくっつけます', 'attr': 'is_remove_doubles'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '重複頂点を結合', 'default': True, 'description': 'UVの切れ目でポリゴンが分かれている仕様なので、インポート時にくっつけます', 'attr': 'is_remove_doubles'}>
is_seam: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'シームをつける', 'default': True, 'description': 'UVの切れ目にシームをつけます', 'attr': 'is_seam'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'シームをつける', 'default': True, 'description': 'UVの切れ目にシームをつけます', 'attr': 'is_seam'}>
is_sharp: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Mark Sharp', 'default': True, 'description': 'This will mark removed doubles on your mesh as sharp (or all free edges if not removing doubles).', 'attr': 'is_sharp'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Mark Sharp', 'default': True, 'description': 'This will mark removed doubles on your mesh as sharp (or all free edges if not removing doubles).', 'attr': 'is_sharp'}>
is_convert_bone_weight_names: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '頂点グループ名をBlender用に変換', 'default': False, 'description': '全ての頂点グループ名をBlenderの左右対称編集で使えるように変換してから読み込みます', 'attr': 'is_convert_bone_weight_names'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '頂点グループ名をBlender用に変換', 'default': False, 'description': '全ての頂点グループ名をBlenderの左右対称編集で使えるように変換してから読み込みます', 'attr': 'is_convert_bone_weight_names'}>
is_vertex_group_sort: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '頂点グループを名前順ソート', 'default': True, 'description': '頂点グループを名前順でソートします', 'attr': 'is_vertex_group_sort'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '頂点グループを名前順ソート', 'default': True, 'description': '頂点グループを名前順でソートします', 'attr': 'is_vertex_group_sort'}>
is_remove_empty_vertex_group: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '割り当てのない頂点グループを削除', 'default': True, 'description': '全ての頂点に割り当てのない頂点グループを削除します', 'attr': 'is_remove_empty_vertex_group'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '割り当てのない頂点グループを削除', 'default': True, 'description': '全ての頂点に割り当てのない頂点グループを削除します', 'attr': 'is_remove_empty_vertex_group'}>
reload_tex_cache: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'テクスチャキャッシュを再構成', 'default': False, 'description': 'texファイルを探す際、キャッシュを再構成します', 'attr': 'reload_tex_cache'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'テクスチャキャッシュを再構成', 'default': False, 'description': 'texファイルを探す際、キャッシュを再構成します', 'attr': 'reload_tex_cache'}>
is_decorate: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '種類に合わせてマテリアルを装飾', 'default': True, 'attr': 'is_decorate'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '種類に合わせてマテリアルを装飾', 'default': True, 'attr': 'is_decorate'}>
is_mate_data_text: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'テキストにマテリアル情報埋め込み', 'default': True, 'description': 'シェーダー情報をテキストに埋め込みます', 'attr': 'is_mate_data_text'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'テキストにマテリアル情報埋め込み', 'default': True, 'description': 'シェーダー情報をテキストに埋め込みます', 'attr': 'is_mate_data_text'}>
is_armature: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'アーマチュア生成', 'default': True, 'description': 'ウェイトを編集する時に役立つアーマチュアを読み込みます', 'attr': 'is_armature'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'アーマチュア生成', 'default': True, 'description': 'ウェイトを編集する時に役立つアーマチュアを読み込みます', 'attr': 'is_armature'}>
is_armature_clean: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '不要なボーンを削除', 'default': False, 'description': 'ウェイトが無いボーンを削除します', 'attr': 'is_armature_clean'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '不要なボーンを削除', 'default': False, 'description': 'ウェイトが無いボーンを削除します', 'attr': 'is_armature_clean'}>
is_custom_bones: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Use Custom Bones', 'default': False, 'description': 'Use the currently selected object for custom bone shapes.', 'attr': 'is_custom_bones'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Use Custom Bones', 'default': False, 'description': 'Use the currently selected object for custom bone shapes.', 'attr': 'is_custom_bones'}>
is_use_local_bones: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Use Local Bones', 'default': True, 'description': 'Use the Local Bone Data for orientation (more accurate)', 'attr': 'is_use_local_bones'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Use Local Bones', 'default': True, 'description': 'Use the Local Bone Data for orientation (more accurate)', 'attr': 'is_use_local_bones'}>
is_bone_data_text: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'テキスト', 'default': True, 'description': 'ボーン情報をテキストとして読み込みます', 'attr': 'is_bone_data_text'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'テキスト', 'default': True, 'description': 'ボーン情報をテキストとして読み込みます', 'attr': 'is_bone_data_text'}>
is_bone_data_obj_property: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'オブジェクトのカスタムプロパティ', 'default': True, 'description': 'メッシュオブジェクトのカスタムプロパティにボーン情報を埋め込みます', 'attr': 'is_bone_data_obj_property'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'オブジェクトのカスタムプロパティ', 'default': True, 'description': 'メッシュオブジェクトのカスタムプロパティにボーン情報を埋め込みます', 'attr': 'is_bone_data_obj_property'}>
is_bone_data_arm_property: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'アーマチュアのカスタムプロパティ', 'default': True, 'description': 'アーマチュアデータのカスタムプロパティにボーン情報を埋め込みます', 'attr': 'is_bone_data_arm_property'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'アーマチュアのカスタムプロパティ', 'default': True, 'description': 'アーマチュアデータのカスタムプロパティにボーン情報を埋め込みます', 'attr': 'is_bone_data_arm_property'}>
texpath_dict = None
@classmethod
def poll(cls, context):
57    @classmethod
58    def poll(cls, context):
59        return True
def invoke(self, context, event):
61    def invoke(self, context, event):
62        prefs = common.preferences()
63        if prefs.model_default_path:
64            self.filepath = common.default_cm3d2_dir(prefs.model_default_path, None, "model")
65        else:
66            self.filepath = common.default_cm3d2_dir(prefs.model_import_path, None, "model")
67        self.scale = prefs.scale
68        self.is_convert_bone_weight_names = prefs.is_convert_bone_weight_names
69        if compat.IS_LEGACY or bpy.app.version < (2, 91):
70            self.is_sharp = False
71        context.window_manager.fileselect_add(self)
72        return {'RUNNING_MODAL'}
def draw(self, context):
 74    def draw(self, context):
 75        prefs = common.preferences()
 76        self.layout.prop(self, 'scale')
 77
 78        box = self.layout.box()
 79        box.prop(self, 'is_mesh', icon='MESH_DATA')
 80
 81        sub_box = box.box()
 82        sub_box.enabled = self.is_mesh
 83        sub_box.label(text="メッシュ")
 84        sub_box.prop(self, 'is_remove_doubles', icon='STICKY_UVS_VERT')
 85        sub_box.prop(self, 'is_seam' , icon=compat.icon('UV_EDGESEL'))
 86        if not compat.IS_LEGACY and bpy.app.version >= (2, 91):
 87            sub_box.prop(self, 'is_sharp', icon=compat.icon('EDGESEL'))
 88
 89        sub_box = box.box()
 90        sub_box.enabled = self.is_mesh
 91        sub_box.label(text="頂点グループ")
 92        sub_box.prop(self, 'is_vertex_group_sort', icon='SORTALPHA')
 93        sub_box.prop(self, 'is_remove_empty_vertex_group', icon='DISCLOSURE_TRI_DOWN')
 94        sub_box.prop(self, 'is_convert_bone_weight_names', icon='BLENDER')
 95
 96        sub_box = box.box()
 97        sub_box.enabled = self.is_mesh
 98        sub_box.label(text="マテリアル")
 99        sub_box.prop(prefs, 'is_replace_cm3d2_tex', icon='BORDERMOVE')
100        sub_box.prop(self, 'reload_tex_cache', icon='FILE_REFRESH')
101        if compat.IS_LEGACY:
102            sub_box.prop(self, 'is_decorate', icon=compat.icon('SHADING_TEXTURE'))
103        sub_box.prop(self, 'is_mate_data_text', icon='TEXT')
104
105        box = self.layout.box()
106        box.prop(self, 'is_armature', icon='ARMATURE_DATA')
107        
108        sub_box = box.box()
109        sub_box.label(text="アーマチュア")
110        sub_box.prop(self , 'is_use_local_bones'          , icon=compat.icon('GROUP_BONE'), text="Use Local Bone Data")
111        sub_box.prop(self , 'is_armature_clean'           , icon=compat.icon('X'         ))
112        sub_box.prop(self , 'is_convert_bone_weight_names', icon=compat.icon('BLENDER'   ), text="ボーン名をBlender用に変換")
113        sub_box.prop(prefs, 'show_bone_in_front'          , icon=compat.icon('HIDE_OFF'  ), text="Show Bones in Front")
114        row = sub_box.row()
115        row.prop    (self , 'is_custom_bones'             , icon=compat.icon('BONE_DATA' ), text="Use Selected as Bone Shape"     )
116        row.enabled = bool(context.object)
117        
118        box = self.layout.box()
119        box.label(text="ボーン情報埋め込み場所")
120        box.prop(self, 'is_bone_data_text', icon='TEXT')
121        box.prop(self, 'is_bone_data_obj_property', icon='OBJECT_DATA')
122        box.prop(self, 'is_bone_data_arm_property', icon='ARMATURE_DATA')
def execute(self, context):
124    def execute(self, context):
125        start_time = time.time()
126
127        prefs = common.preferences()
128        prefs.model_import_path = self.filepath
129        prefs.scale = self.scale
130        context.window_manager.progress_begin(0, 10)
131        context.window_manager.progress_update(0)
132
133        custom_bone_ob = context.active_object
134        if not custom_bone_ob:
135            self.is_custom_bones = False
136
137        #global_matrix = bpy_extras.io_utils.axis_conversion(from_forward=self.axis_forward, from_up=self.axis_up).to_4x4()
138
139        try:
140            reader = open(self.filepath, 'rb')
141        except:
142            self.report(type={'ERROR'}, message=f_tip_("ファイルを開くのに失敗しました、アクセス不可かファイルが存在しません。file={}", self.filepath))
143            return {'CANCELLED'}
144
145        self.texpath_dict = common.get_texpath_dict(reload=self.reload_tex_cache)
146
147        with reader:
148            # ヘッダー
149            ext = None
150            try: # luvoid : utf-8 decoding could possibly throw an error here
151                ext = common.read_str(reader)
152            except:
153                ext = False
154            if ext != 'CM3D2_MESH':
155                self.report(type={'ERROR'}, message="これはカスタムメイド3D2のモデルファイルではありません")
156                return {'CANCELLED'}
157            model_ver = struct.unpack('<i', reader.read(4))[0]
158            self.report(type={'INFO'}, message=f_tip_("Model Version = {version}", version=model_ver))
159            context.window_manager.progress_update(0.1)
160
161            try:
162                # 名前群を取得
163                model_name1 = common.read_str(reader)
164                model_name2 = common.read_str(reader)
165                context.window_manager.progress_update(0.2)
166
167                # ボーン情報読み込み
168                bone_data = []
169                bone_count = struct.unpack('<i', reader.read(4))[0]
170                for i in range(bone_count):
171                    name = common.read_str(reader)
172                    scl = struct.unpack('<B', reader.read(1))[0]
173                    bone_data.append({'name': name, 'scl': scl})
174
175                for i in range(bone_count):
176                    parent_index = struct.unpack('<i', reader.read(4))[0]
177                    parent_name = None
178                    if parent_index != -1:
179                        parent_name = bone_data[parent_index]['name']
180                    bone_data[i]['parent_index'] = parent_index
181                    bone_data[i]['parent_name'] = parent_name
182
183                for i in range(bone_count):
184                    x, y, z = struct.unpack('<3f', reader.read(3*4))
185                    bone_data[i]['co'] = mathutils.Vector((x, y, z))
186
187                    x, y, z = struct.unpack('<3f', reader.read(3*4))
188                    w = struct.unpack('<f', reader.read(4))[0]
189                    bone_data[i]['rot'] = mathutils.Quaternion((w, x, y, z))
190                    if model_ver >= 2001:
191                        use_scale = struct.unpack('<B', reader.read(1))[0]
192                        if use_scale:
193                            print(bone_data[i]['name'],"has scale data!")
194                            scale_x, scale_y, scale_z = struct.unpack('<3f', reader.read(3*4))
195                            bone_data[i]['scale'] = [scale_x, scale_y, scale_z]
196
197                context.window_manager.progress_update(0.3)
198
199                print(f_("Reading vertex, mesh, and local bone count at 0x{num:02X}", num=reader.tell()))
200                vertex_count, mesh_count, local_bone_count = struct.unpack('<3i', reader.read(3*4))
201
202                # ローカルボーン情報読み込み
203                local_bone_data = []
204                for i in range(local_bone_count):
205                    local_bone_data.append({'name': common.read_str(reader)})
206
207                for i in range(local_bone_count):
208                    row0 = struct.unpack('<4f', reader.read(4 * 4))
209                    row1 = struct.unpack('<4f', reader.read(4 * 4))
210                    row2 = struct.unpack('<4f', reader.read(4 * 4))
211                    row3 = struct.unpack('<4f', reader.read(4 * 4))
212                    local_bone_data[i]['matrix'] = mathutils.Matrix([row0, row1, row2, row3])
213                context.window_manager.progress_update(0.4)
214
215                # 頂点情報読み込み
216                vertex_data = []
217                print(f_("Reading vertex data at 0x{num:02X}", num=reader.tell()))
218                extra_uv_uses = [False] * 7
219                if model_ver >= 2102: # CR Edit Mode
220                    extra_uv_uses = struct.unpack('<7?', reader.read(7))
221                    print(f_("extra_uv_uses = {boollist}", boollist=extra_uv_uses))
222                for i in range(vertex_count):
223                    co = struct.unpack('<3f', reader.read(3 * 4))
224                    no = struct.unpack('<3f', reader.read(3 * 4))
225                    uv = struct.unpack('<2f', reader.read(2 * 4))
226                    extra_uvs = [] # CR Edit
227                    for i, used in enumerate(extra_uv_uses):
228                        if used:
229                            extra_uvs.append(struct.unpack('<2f', reader.read(2 * 4)))
230                    vertex_data.append({'co': co, 'normal': no, 'uv': uv, 'extra_uvs': extra_uvs})
231                if self.is_remove_doubles:
232                    comparison_data = list(hash(repr(v['co']) + " " + repr(v['normal'])) for v in vertex_data)
233                    comparison_counter = Counter(comparison_data)
234                    comparison_data = list((comparison_counter[h] > 1) for h in comparison_data)
235                    del comparison_counter
236                print(f_("Reading unknown count at 0x{num:02X}", num=reader.tell()))
237                unknown_count = struct.unpack('<i', reader.read(4))[0]
238                for i in range(unknown_count):
239                    struct.unpack('<4f', reader.read(4 * 4))
240                for i in range(vertex_count):
241                    indexes = struct.unpack('<4H', reader.read(4 * 2))
242                    values = struct.unpack('<4f', reader.read(4 * 4))
243                    vertex_data[i]['weights'] = list({
244                            'index': index,
245                            'value': value,
246                            'name': local_bone_data[index]['name'],
247                        } for index, value in zip(indexes, values))
248                context.window_manager.progress_update(0.5)
249                # 面情報読み込み
250                face_data = []
251                for i in range(mesh_count):
252                    face_count = int(struct.unpack('<i', reader.read(4))[0] / 3)
253                    datum = [tuple(reversed(struct.unpack('<3H', reader.read(3 * 2)))) for j in range(face_count)]
254                    face_data.append(datum)
255                context.window_manager.progress_update(0.6)
256
257                # マテリアル情報読み込み
258                # TODO MaterialHandlerに変更
259                material_data = []
260                material_count = struct.unpack('<i', reader.read(4))[0]
261                for i in range(material_count):
262                    print(f_("mate count: {num} of {count} @ 0x{pos:02X}", num=i, count=material_count, pos=reader.tell()))
263                    data = cm3d2_data.MaterialHandler.read(reader, read_header=False, version=model_ver)
264                    data.name1 = data.name.lower()
265                    material_data.append(data)
266                    
267                    # name1 = common.read_str(reader)
268                    # name2 = common.read_str(reader)
269                    # name3 = common.read_str(reader)
270                    # data_list = []
271                    # material_data.append({'name1': name1, 'name2': name2, 'name3': name3, 'data': data_list})
272                    # while True:
273                    #     data_type = common.read_str(reader)
274                    #     if data_type == 'tex':
275                    #         data_item = {'type': data_type}
276                    #         data_list.append(data_item)
277                    #         data_item['name'] = common.read_str(reader)
278                    #         data_item['type2'] = common.read_str(reader)
279                    #         if data_item['type2'] == 'tex2d':
280                    #             data_item['name2'] = common.read_str(reader)
281                    #             data_item['path'] = common.read_str(reader)
282                    #             data_item['tex_map'] = struct.unpack('<4f', reader.read(4*4))
283                    #     elif data_type == 'col':
284                    #         name = common.read_str(reader)
285                    #         col = struct.unpack('<4f', reader.read(4*4))
286                    #         data_list.append({'type': data_type, 'name': name, 'color': col})
287                    #     elif data_type == 'f':
288                    #         name = common.read_str(reader)
289                    #         fval = struct.unpack('<f', reader.read(4))[0]
290                    #         data_list.append({'type': data_type, 'name': name, 'float': fval})
291                    #     else:
292                    #         break
293
294                context.window_manager.progress_update(0.8)
295
296                # その他情報読み込み
297                misc_data = []
298                while True:
299                    #print(f_("Reading data_type at 0x{num:02X}", num=reader.tell()))
300                    data_type = common.read_str(reader)
301                    if data_type == 'morph':
302                        misc_item = {'type': data_type}
303                        misc_data.append(misc_item)
304                        misc_item['name'] = common.read_str(reader)
305                        misc_item['data'] = data_list = []
306                        morph_vert_count = struct.unpack('<i', reader.read(4))[0]
307                        morph_extra_uvs = False
308                        if model_ver >= 2102: # CR Edit Mode
309                            morph_extra_uvs = struct.unpack('<?', reader.read(1))[0]
310                            misc_item['uvs'] = []
311                            print(f_("{morph}.morph_extra_uvs @ 0x{pos:02X} = {bool}", morph=misc_item['name'], bool=morph_extra_uvs, pos=reader.tell()-1))
312                        for i in range(morph_vert_count):
313                            index = struct.unpack('<H', reader.read(2))[0]
314                            co = mathutils.Vector(struct.unpack('<3f', reader.read(3 * 4)))
315                            normal = struct.unpack('<3f', reader.read(3 * 4))
316                            extra_uvs = () # CR Edit
317                            if morph_extra_uvs:
318                                extra_uvs = struct.unpack('<4f', reader.read(4 * 4))
319                            data_list.append({'index': index, 'co': co, 'normal': normal, 'color': extra_uvs})
320                    else:
321                        break
322            
323            except UnicodeDecodeError as e:
324                msg = [
325                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()-len(e.object)) + "\n",
326                    str(e) + "\n",
327                    *traceback.format_tb(e.__traceback__)
328                ]
329                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
330                print("".join(msg))
331                return {'CANCELLED'}
332            
333            except struct.error as e:
334                msg = [
335                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()) + "\n",
336                    str(e) + "\n",
337                    *traceback.format_tb(e.__traceback__)
338                ]
339                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
340                print("".join(msg))
341                return {'CANCELLED'}
342
343            except common.CM3D2ImportException as e:
344                msg = [
345                    f_tip_("Error reading file at byte 0x{num:02X}", num=reader.tell()) + "\n",
346                    str(e) + "\n",
347                    *traceback.format_tb(e.__traceback__)
348                ]
349                self.report(type={'ERROR'}, message="".join(reversed(msg))[0:-1])
350                print("".join(msg))
351                return {'CANCELLED'}
352
353        context.window_manager.progress_update(1)
354
355        try:
356            bpy.ops.object.mode_set(mode='OBJECT')
357        except RuntimeError:
358            pass
359        bpy.ops.object.select_all(action='DESELECT')
360
361        # アーマチュア作成
362        if self.is_armature:
363            arm    = bpy.data.armatures.new(model_name1 + ".armature")
364            arm_ob = bpy.data.objects.new  (model_name1 + ".armature", arm)
365            compat.link(bpy.context.scene, arm_ob)
366            compat.set_select(arm_ob, True)
367            compat.set_active(context, arm_ob)
368
369            arm.show_names              = prefs.show_bone_names        
370            arm.show_axes               = prefs.show_bone_axes         
371            arm.show_bone_custom_shapes = prefs.show_bone_custom_shapes
372            arm.show_group_colors       = prefs.show_bone_group_colors
373            if compat.IS_LEGACY:
374                arm_ob.show_x_ray = prefs.show_bone_in_front
375            else:
376                arm_ob.show_in_front = prefs.show_bone_in_front     
377
378            bpy.ops.object.mode_set(mode='EDIT')
379
380            is_odd_scale_bone = False
381
382            # 基幹ボーンのみ作成
383            child_data = []
384            for data in bone_data:
385                if not data['parent_name']:
386                    bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
387                    bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
388                    bone.use_deform = False
389
390                    #co.x, co.y, co.z = -co.x, co.z, -co.y
391                    #rot = compat.mul(rot, mathutils.Quaternion((0, 0, 1), math.radians(90)))
392                    #rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, rot.z, -rot.y
393                    
394                    #co  = data['co' ].copy()
395                    #rot = data['rot'].copy()
396                    #co.x, co.y, co.z = -co.x, -co.z, co.y
397                    #rot.w, rot.x, rot.y, rot.z = -rot.w, -rot.x, -rot.z, rot.y
398                    ##rot = compat.mul(rot, mathutils.Quaternion((0, 0, 1), math.radians(-90)))
399                    #rot = compat.convert_cm_to_bl_bone_rotation(rot)
400                    #mat = compat.mul(mathutils.Matrix.Translation(co), rot.to_matrix().to_4x4())
401                    
402                    co_mat  = mathutils.Matrix.Translation(data['co'].copy() * self.scale)
403                    rot     = mathutils.Quaternion(data['rot'].copy())
404                    #rot     = compat.convert_cm_to_bl_bone_rotation(rot)
405                    rot_mat = rot.to_matrix().to_4x4()
406                    #rot_mat = compat.convert_cm_to_bl_bone_rotation(rot_mat)
407                    mat = compat.mul(co_mat, rot_mat)
408                    mat = compat.convert_cm_to_bl_bone_rotation(mat)
409                    mat = compat.convert_cm_to_bl_space(mat)
410                    #mat = compat.mul(mat, compat.CM_TO_BL_LOCAL_BONE_MAT4)
411                    
412                    
413                    #fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
414                    #fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
415                    #fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
416
417                    #compat.set_bone_matrix(bone, compat.mul4(fix_mat_scale, fix_mat_before, mat, fix_mat_after))
418                    compat.set_bone_matrix(bone, mat)
419
420
421                    bone["cm3d2_scl_bone"] = 1 if data['scl'] else 0
422                    if 'scale' in data:
423                        bone['cm3d2_bone_scale'] = data['scale']
424                        scale = mathutils.Vector(data['scale'])
425                        if ( scale - mathutils.Vector((1,1,1)) ).length > 1e-5:
426                            is_odd_scale_bone = True
427                            self.report(type={'WARNING'}, message=f_tip_("Bone '{bone_name}' has odd scale '{bone_scale}' (odd by {bone_diff})", bone_name=bone.name, bone_scale=scale, bone_diff=( scale - mathutils.Vector((1,1,1)) ).length))
428                        scale *= self.scale * 0.01
429                        scale = compat.convert_cm_to_bl_bone_rotation(scale)
430                        bone.bbone_x = scale.x
431                        bone.bbone_z = scale.z
432                        #look = bone.tail - bone.head
433                        #look *= scale.y
434                        #bone.tail = look + bone.head
435                else:
436                    child_data.append(data)
437            context.window_manager.progress_update(1.333)
438
439            # 子ボーンを追加していく
440            while len(child_data):
441                data = child_data.pop(0)
442                parent = arm.edit_bones.get(common.decode_bone_name(data['parent_name'], self.is_convert_bone_weight_names))
443                if parent:
444                    bone = arm.edit_bones.new(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
445                    bone.parent = parent
446                    bone.head, bone.tail = (0, 0, 0), (0, 1, 0)
447                    bone.use_deform = False
448
449                    #parent_mats = []
450                    #current_bone = bone
451                    #while current_bone:
452                    #    for b in bone_data:
453                    #        if common.decode_bone_name(b['name'], self.is_convert_bone_weight_names) == current_bone.name:
454                    #            local_co  = b['co' ].copy()
455                    #            local_rot = b['rot'].copy()
456                    #            break
457                    #
458                    #    local_co_mat  = mathutils.Matrix.Translation(local_co)
459                    #    local_rot_mat = local_rot.to_matrix().to_4x4()        
460                    #    parent_mats.append(compat.mul(local_co_mat, local_rot_mat))
461                    #
462                    #    current_bone = current_bone.parent
463                    #parent_mats.reverse()
464                    #
465                    #mat = mathutils.Matrix()
466                    #for local_mat in parent_mats:
467                    #    mat = compat.mul(mat, local_mat)
468                    #mat *= self.scale
469                    #mat = compat.convert_cm_to_bl_space(mat)
470                    #mat = compat.convert_cm_to_bl_bone_rotation(mat)
471                     
472                    #parent_mat    = compat.mul(
473                    #    mathutils.Matrix.Translation(parent.matrix.to_translation()),
474                    #    parent.matrix.to_quaternion().to_matrix().to_4x4()
475                    #)
476
477                    parent_mat = parent.matrix
478                    
479                    local_co      = data['co' ].copy() * self.scale
480                    local_rot     = data['rot'].copy()
481                    #local_rot     = compat.convert_cm_to_bl_bone_rotation(rot)
482                    local_co_mat  = mathutils.Matrix.Translation(local_co)
483                    local_rot_mat = local_rot.to_matrix().to_4x4()
484                    local_mat     = compat.mul(local_co_mat, local_rot_mat)
485                    local_mat     = compat.convert_cm_to_bl_bone_space(local_mat)
486                    #local_mat     = compat.mul(local_mat, mathutils.Matrix.Diagonal((1,1,1)).to_4x4())
487                    mat = compat.mul(parent_mat, local_mat)
488                    mat = compat.convert_cm_to_bl_bone_rotation(mat)
489
490                    
491                    #co_mat        = compat.mul( parent.matrix.inverted(), compat.convert_cm_to_bl_local_bone_mat4(local_co_mat) )
492                    #rot_mat       = compat.mul( local_rot_mat, compat.CM_TO_BL_LOCAL_BONE_MAT4 )
493                    #mat           = compat.ul(co_mat, rot_mat)
494                    #local_mat = compat.mul(local_co_mat, local_rot_mat)
495                    #local_mat *= self.scale
496                    #mat = compat.mul( parent.matrix, compat.convert_cm_to_bl_local_bone(local_mat) )
497                    #mat *= self.scale
498
499                    #mat *= self.scale
500
501                    #co.x, co.y, co.z = -co.y, co.z, co.x
502                    #rot.w, rot.x, rot.y, rot.z = rot.w, rot.y, -rot.z, -rot.x
503
504                    #co  = data['co' ].copy() * self.scale
505                    #rot = data['rot'].copy()
506                    #co.x, co.y, co.z = co.z, -co.x, co.y
507                    ##co = compat.convert_cm_to_bl_local_bone(co)
508                    ##rot.w, rot.x, rot.y, rot.z = rot.w, -rot.z, rot.x, -rot.y 
509                    ##rot.w, rot.x, rot.y, rot.z = rot.w, -rot.z, rot.x, -rot.y
510                    #local_mat = compat.mul(mathutils.Matrix.Translation(co), rot.to_matrix().to_4x4())
511                    #mat = compat.mul( parent.matrix, local_mat )
512                    ##mat *= self.scale
513
514                    #fix_mat_scale = mathutils.Matrix.Scale(-1, 4, (1, 0, 0))
515                    #fix_mat_before = mathutils.Euler((math.radians(90), 0, 0), 'XYZ').to_matrix().to_4x4()
516                    #fix_mat_after = mathutils.Euler((0, 0, math.radians(90)), 'XYZ').to_matrix().to_4x4()
517
518                    #compat.set_bone_matrix(bone, compat.mul4(fix_mat_scale, fix_mat_before, mat, fix_mat_after))
519                    compat.set_bone_matrix(bone, mat)
520                    
521                    bone['cm3d2_scl_bone'] = 1 if data['scl'] else 0
522                    if 'scale' in data:
523                        bone['cm3d2_bone_scale'] = data['scale']
524                        scale = mathutils.Vector(data['scale'])
525                        if ( scale - mathutils.Vector((1,1,1)) ).length > 1e-5:
526                            is_odd_scale_bone = True
527                            self.report(type={'WARNING'}, message=f_tip_("Bone '{bone_name}' has odd scale '{bone_scale}' (odd by {bone_diff})", bone_name=bone.name, bone_scale=scale, bone_diff=( scale - mathutils.Vector((1,1,1)) ).length))
528                        scale *= self.scale * 0.01
529                        bone.bbone_x = scale.x
530                        bone.bbone_z = scale.z
531                        #bone.bbone_segments = scale.y
532                        #look = bone.tail - bone.head
533                        #look *= scale.y
534                        #bone.tail = look + bone.head
535                else:
536                    child_data.append(data)
537            context.window_manager.progress_update(1.666)
538            
539            # Configure bones in local bone data
540            is_local_bones_corrupt = False
541            base_bone = arm.edit_bones.get(common.decode_bone_name(model_name2, self.is_convert_bone_weight_names))
542            base_bone_offset = base_bone.matrix.copy()
543            base_bone_offset = compat.mul(mathutils.Matrix.Scale(-1, 4, (1, 0, 0)), base_bone_offset)
544            base_bone_offset = compat.convert_bl_to_cm_bone_rotation(base_bone_offset)
545            print(base_bone_offset)
546            print(f"base_bone_offset @ I =\n{base_bone_offset @ mathutils.Matrix.Identity(4)}")
547            #base_bone_offset = mathutils.Matrix.Identity(4) # compat.mul(base_bone_mat.inverted(), base_bone_mat)
548
549            def setup_local_bone(bone, mat, isRoot=False):
550                pos = compat.transform_inverse(mat.transposed()).translation
551                mat.row[3] = (0.0, 0.0, 0.0, 1.0)
552                mat.translation = pos
553                mat.translation *= self.scale
554                offset_mat = mat.copy()
555                if not common.is_descendant_of(bone, base_bone):
556                    mat = compat.mul(base_bone_offset, mat)
557                    #mat.translation = mat.translation + base_bone_offset.translation
558                mat = compat.convert_cm_to_bl_bone_rotation(mat)
559                mat = compat.mul(mathutils.Matrix.Scale(-1, 4, (1, 0, 0)), mat)
560                
561            
562                # The matrices from the local bone data are more precise rotations, but make sure they aren't corrupted
563                old_pos, old_rot, old_scale = bone.matrix.decompose()
564                compat.set_bone_matrix(bone, mat)
565                new_pos, new_rot, new_scale = bone.matrix.decompose()
566                dif_pos = (new_pos-old_pos).length/self.scale
567                dif_rot = old_rot.rotation_difference(new_rot)
568                if dif_pos > 0.1 or dif_rot.w < .9:
569                    print(dif_pos,  dif_rot)
570                    is_local_bones_corrupt = True
571                    #self.report(type={'WARNING'}, message="Found potentially corrupt local bone data, please re-import with \"Use Local Bone Data\" disabled.")
572                return mat
573
574            for data in local_bone_data:
575                if self.is_use_local_bones and data['name'] == model_name2:
576                    bone = arm.edit_bones.get(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
577                    mat = mathutils.Matrix(data['matrix'])
578                    print("Found base bone in local bone data!")
579                    #base_bone_offset = compat.mul(compat.transform_inverse(base_bone_offset), setup_local_bone(bone, mat, isRoot=True))
580
581
582            for data in local_bone_data:
583                bone = arm.edit_bones.get(common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
584                bone.use_deform = True
585                if self.is_use_local_bones and not data['name'] == model_name2:
586                    mat = mathutils.Matrix(data['matrix'])
587                    setup_local_bone(bone, mat)
588            
589            
590            def distOnRay(pos0, pos1, point):
591                w = point - pos0
592                d = (pos1 - pos0).normalized()
593                return w.dot(d) / d.dot(d)
594            
595            # ボーン整頓
596            for bone in arm.edit_bones:
597                if len(bone.children) == 0:
598                    if bone.parent:
599                        bone.length = bone.parent.length * 0.5
600                    else:
601                        bone.length = 0.2 * self.scale
602                elif len(bone.children) == 1:
603                    co = bone.children[0].head - bone.head
604                    bone.length = co.length
605                elif len(bone.children) >= 2:
606                    if bone.parent:
607                        max_len = 0.0
608                        for child_bone in bone.children:
609                            if "Pelvis" in bone.name:
610                                dist = (child_bone.head - bone.head).length
611                            else:
612                                dist = distOnRay(bone.head, bone.tail, child_bone.head)
613                            if dist > max_len:
614                                max_len = dist
615                        bone.length = max_len
616                    else:
617                        bone.length = 0.2 * self.scale
618            for bone in arm.edit_bones:
619                if len(bone.children) == 0:
620                    if bone.parent:
621                        bone.length = bone.parent.length * 0.5
622            
623            # Make sure no bones are length 0, otherwise blender deletes them
624            for bone in arm.edit_bones:
625                min_length = 0.0001
626                if bone.length < min_length:
627                    bone.length = min_length
628            
629            # 一部ボーン削除
630            if self.is_armature_clean:
631                for bone in arm.edit_bones:
632                    for b in local_bone_data:
633                        name = common.decode_bone_name(b['name'], self.is_convert_bone_weight_names)
634                        if bone.name == name:
635                            break
636                    else:
637                        arm.edit_bones.remove(bone)
638
639            arm.layers[16] = True
640            compat.set_display_type(arm, prefs.bone_display_type)
641            bpy.ops.armature.select_all(action='DESELECT')
642            bpy.ops.object.mode_set(mode='OBJECT')
643            if self.is_custom_bones:
644                print("Set custom bones")
645                for pose_bone in arm_ob.pose.bones:
646                    pose_bone.custom_shape = custom_bone_ob
647        context.window_manager.progress_update(2)
648
649        if self.is_mesh:
650            ob, me = self.create_mesh(context, model_name1, vertex_data, face_data)
651            # オブジェクト変形
652            CNV_OT_align_to_cm3d2_base_bone.from_bone_data(ob, bone_data, local_bone_data, base_bone_name=model_name2, scale=self.scale)
653            context.window_manager.progress_update(3)
654            
655            self.create_uvs(context, me, vertex_data, extra_uv_uses)
656            context.window_manager.progress_update(4)
657
658            self.create_vertex_groups(context, ob, vertex_data, local_bone_data)
659            context.window_manager.progress_update(5)
660
661            self.create_shapekeys(context, ob, misc_data)
662            context.window_manager.progress_update(6)
663
664            # マテリアル追加
665            progress_count_total = 0.0
666            for data in material_data:
667                progress_count_total += 1 #len(data['data'])
668            self.progress_plus_value = 1.0 / (progress_count_total if progress_count_total > 0.0 else 1.0)
669            self.progress_count = 6.0
670
671            face_seek = 0
672            mates_set = set()
673            override = context.copy()
674            override['object'] = ob
675            prefs = common.preferences()
676            for index, data in enumerate(material_data):
677                print(f_("material count: {num} of {count}", num=index, count=material_count))
678                if prefs.mate_unread_same_value and data.name in mates_set:
679                    continue
680                mates_set.add(data.name)
681                #common.preferences().mate_unread_same_value
682                bpy.ops.object.material_slot_add(override)
683                mate = context.blend_data.materials.new(data.name)#['name1'])
684                #mate['shader1'] = data['name2']
685                #mate['shader2'] = data['name3']
686
687                ob.material_slots[-1].material = mate
688                # 面にマテリアル割り当て
689                for i in range(face_seek, face_seek + len(face_data[index])):
690                    me.polygons[i].material_index = index
691                face_seek += len(face_data[index])
692
693                # テクスチャ追加
694                if compat.IS_LEGACY:
695                    #self.create_mateprop_old(context, me, texes_set, mate, index, data)
696                    cm3d2_data.MaterialHandler.apply_to_old(override, mate, data)
697                    common.decorate_material(mate, self.is_decorate, me, index)
698                else:
699                    #self.create_mateprop(context, me, texes_set, mate, index, data)
700                    cm3d2_data.MaterialHandler.apply_to(override, mate, data)
701                    common.decorate_material(mate, self.is_decorate, me, index)
702                common.setup_material(mate)
703
704            ob.active_material_index = 0
705            context.window_manager.progress_update(7)
706
707            # メッシュ整頓
708            pre_mesh_select_mode = context.tool_settings.mesh_select_mode[:]
709            
710            # Too buggy on versions before 2.91 so just disable it outright
711            #if self.is_sharp and (compat.IS_LEGACY or bpy.app.version < (2, 91)):
712            #    context.tool_settings.mesh_select_mode = (False, True, False)
713            #    bpy.ops.object.mode_set(mode='EDIT')
714            #    
715            #    bpy.ops.mesh.select_non_manifold(extend=False, use_wire=True, use_boundary=True, use_multi_face=False, use_non_contiguous=False, use_verts=False)
716            #    for is_comparison, vert in zip(comparison_data, me.vertices):
717            #        if is_comparison:
718            #            vert.select = False
719            #    bpy.ops.mesh.mark_sharp(use_verts=False)
720            #
721            #    bpy.ops.object.mode_set(mode='OBJECT')
722
723            can_mark_sharp = not compat.IS_LEGACY and bpy.app.version >= (2, 91)
724
725            if self.is_remove_doubles:
726                context.tool_settings.mesh_select_mode = (True, False, False)
727                bpy.ops.object.mode_set(mode='EDIT')
728                if not self.is_sharp or not can_mark_sharp:
729                    bpy.ops.mesh.select_all(action='DESELECT')
730                    bpy.ops.object.mode_set(mode='OBJECT')
731                    for is_comparison, vert in zip(comparison_data, me.vertices):
732                        if is_comparison:
733                            vert.select = True
734                    bpy.ops.object.mode_set(mode='EDIT')
735                else:
736                    bpy.ops.mesh.select_all(action='SELECT')
737                
738                if not can_mark_sharp:
739                    bpy.ops.mesh.remove_doubles(threshold=0.000001/5 * self.scale)
740                else:
741                    bpy.ops.mesh.remove_doubles(threshold=0.000001/5 * self.scale, use_sharp_edge_from_normals=self.is_sharp)
742                bpy.ops.object.mode_set(mode='OBJECT')
743            
744            context.tool_settings.mesh_select_mode = pre_mesh_select_mode
745
746            if self.is_seam:
747                bpy.ops.object.mode_set(mode='EDIT')
748                bpy.ops.mesh.select_all(action='SELECT')
749                bpy.ops.uv.select_all(action='SELECT')
750                bpy.ops.uv.seams_from_islands()
751                bpy.ops.object.mode_set(mode='OBJECT')
752            bpy.ops.object.mode_set(mode='EDIT')
753            bpy.ops.mesh.select_all(action='DESELECT')
754            bpy.ops.object.mode_set(mode='OBJECT')
755
756            if self.is_armature:
757                mod = ob.modifiers.new("Armature", 'ARMATURE')
758                mod.object = arm_ob
759                compat.set_active(context, arm_ob)
760                bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
761                compat.set_active(context, ob)
762        context.window_manager.progress_update(8)
763
764        # マテリアル情報のテキスト埋め込み
765        if self.is_mate_data_text:
766            for index, data in enumerate(material_data):
767                txt_name = "Material:" + str(index)
768                if txt_name in context.blend_data.texts:
769                    txt = context.blend_data.texts[txt_name]
770                    txt.clear()
771                else:
772                    txt = context.blend_data.texts.new(txt_name)
773                txt.write(data.to_text())
774                
775                # txt.write("1000" + "\n")
776                # txt.write(data['name1'].lower() + "\n")
777                # txt.write(data['name1'] + "\n")
778                # txt.write(data['name2'] + "\n")
779                # txt.write(data['name3'] + "\n")
780                # txt.write("\n")
781                # for tex_data in data['data']:
782                #     txt.write(tex_data['type'] + "\n")
783                #     if tex_data['type'] == 'tex':
784                #         txt.write("\t" + tex_data['name'] + "\n")
785                #         txt.write("\t" + tex_data['type2'] + "\n")
786                #         if tex_data['type2'] == 'tex2d':
787                #             txt.write("\t" + tex_data['name2'] + "\n")
788                #             txt.write("\t" + tex_data['path'] + "\n")
789                #             map_list = tex_data['tex_map']
790                #             tex_map = " ".join([str(map_list[0]), str(map_list[1]), str(map_list[2]), str(map_list[3])])
791                #             txt.write("\t" + tex_map + "\n")
792                #     elif tex_data['type'] == 'col':
793                #         txt.write("\t" + tex_data['name'] + "\n")
794                #         col = " ".join([str(tex_data['color'][0]), str(tex_data['color'][1]), str(tex_data['color'][2]), str(tex_data['color'][3])])
795                #         txt.write("\t" + col + "\n")
796                #     elif tex_data['type'] == 'f':
797                #         txt.write("\t" + tex_data['name'] + "\n")
798                #         txt.write("\t" + str(tex_data['float']) + "\n")
799                # txt.current_line_index = 0
800        context.window_manager.progress_update(9)
801
802        # ボーン情報のテキスト埋め込み
803        if self.is_bone_data_text:
804            if "BoneData" in context.blend_data.texts:
805                txt = context.blend_data.texts["BoneData"]
806                txt.clear()
807            else:
808                txt = context.blend_data.texts.new("BoneData")
809        for i, data in enumerate(bone_data):
810            s = ",".join([data['name'], str(data['scl']), ""])
811            parent_index = data['parent_index']
812            if -1 < parent_index:
813                s += bone_data[parent_index]['name'] + ","
814            else:
815                s += "None" + ","
816            s += " ".join([str(data['co'][0]), str(data['co'][1]), str(data['co'][2])]) + ","
817            s += " ".join([str(data['rot'][0]), str(data['rot'][1]), str(data['rot'][2]), str(data['rot'][3])])
818            if model_ver >= 2001:
819                if 'scale' in data:
820                    s += ",1," + " ".join(map(str, data['scale']))
821                else:
822                    s += ",0"
823
824            if self.is_bone_data_text:
825                txt.write(s + "\n")
826            if self.is_mesh and self.is_bone_data_obj_property:
827                ob["BoneData:" + str(i)] = s
828            if self.is_armature and self.is_bone_data_arm_property:
829                arm["BoneData:" + str(i)] = s
830        if self.is_bone_data_text:
831            txt['BaseBone'] = model_name2
832            txt.current_line_index = 0
833        context.window_manager.progress_update(10)
834
835        # ローカルボーン情報のテキスト埋め込み
836        if self.is_bone_data_text:
837            if "LocalBoneData" in context.blend_data.texts:
838                txt = context.blend_data.texts["LocalBoneData"]
839                txt.clear()
840            else:
841                txt = context.blend_data.texts.new("LocalBoneData")
842        for i, data in enumerate(local_bone_data):
843            s = data['name'] + ","
844
845            mat_list = list(data['matrix'][0])
846            mat_list.extend(list(data['matrix'][1]))
847            mat_list.extend(list(data['matrix'][2]))
848            mat_list.extend(list(data['matrix'][3]))
849            for j, f in enumerate(mat_list):
850                mat_list[j] = str(f)
851            s += " ".join(mat_list)
852
853            if self.is_bone_data_text:
854                txt.write(s + "\n")
855            if self.is_mesh and self.is_bone_data_obj_property:
856                ob["LocalBoneData:" + str(i)] = s
857            if self.is_armature and self.is_bone_data_arm_property:
858                arm["LocalBoneData:" + str(i)] = s
859        if self.is_bone_data_text:
860            txt['BaseBone'] = model_name2
861            txt.current_line_index = 0
862
863        if self.is_mesh and self.is_bone_data_obj_property:
864            ob['BaseBone'] = model_name2
865            if model_ver >= 1000:
866                ob['ModelVersion'] = model_ver
867        if self.is_armature and self.is_bone_data_arm_property:
868            arm['BaseBone'] = model_name2
869            if model_ver >= 1000:
870                arm['ModelVersion'] = model_ver
871        context.window_manager.progress_end()
872
873        require_time = time.time() - start_time
874        filesize = os.path.getsize(self.filepath)
875        filesize_str = "バイト"
876        if 1024 * 1024 < filesize:
877            filesize = filesize / (1024 * 1024.0)
878            filesize_str = "MB"
879        elif 1024 < filesize:
880            filesize = filesize / 1024.0
881            filesize_str = "KB"
882        self.report(type={'INFO'}, message=f_tip_("modelのインポートが完了しました ({} {}/ {:.2f} 秒)", filesize, filesize_str, require_time))
883        
884        if is_odd_scale_bone:
885            self.report(type={'WARNING'}, message="Found bone with a scale not equal to 1.")
886        if is_local_bones_corrupt:
887            self.report(type={'ERROR'}, message="Found potentially corrupt local bone data, please re-import with \"Use Local Bone Data\" disabled.")
888        return {'FINISHED'}
def create_mesh( self, context, model_name1, vertex_data, face_data) -> (<class 'bpy_types.Object'>, <class 'bpy_types.Mesh'>):
890    def create_mesh(self, context, model_name1, vertex_data, face_data) -> (bpy.types.Object, bpy.types.Mesh):
891        # メッシュ作成
892        me = context.blend_data.meshes.new(model_name1)
893        verts, faces = [], []
894        for data in vertex_data:
895            #co = list(data['co'][:])
896            #co[0] = -co[0]
897            #co[0] *= self.scale
898            #co[1] *= self.scale
899            #co[2] *= self.scale
900            co = compat.convert_cm_to_bl_space( mathutils.Vector(data['co']) * self.scale )
901            #co = mathutils.Vector(data['co']) * self.scale
902            verts.append(co)
903        context.window_manager.progress_update(2.25)
904        for data in face_data:
905            faces.extend(data)
906        context.window_manager.progress_update(2.5)
907        me.from_pydata(verts, [], faces)
908
909        # オブジェクト化
910        ob = context.blend_data.objects.new(model_name1, me)
911        ob.rotation_mode = 'QUATERNION'
912        compat.link(context.scene, ob)
913        compat.set_select(ob, True)
914        compat.set_active(context, ob)
915        bpy.ops.object.shade_smooth()
916        context.window_manager.progress_update(2.75)
917
918        # Custom Split Normals
919        #normals_color = me.vertex_colors.new(name=f"Basis_normals", do_init=False) or me.vertex_colors[-1]
920        #for vert_index, vert in enumerate(vertex_data):
921        #    no = compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']) ) #mathutils.Vector(vert['normal']) * mathutils.Vector((-1,1,1)) #
922        #    no.normalize()
923        #    print(no)
924        #    for loop_index in vert_loops[vert_index]:
925        #        #normals[loop_index] = no
926        #        normals_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
927        #            *no, #*(no * 0.5 + mathutils.Vector([0.5]*3)),
928        #            1,
929        #        )
930        #me.normals_split_custom_set(normals)
931        me.normals_split_custom_set_from_vertices(
932            tuple(
933                compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']) )
934                for vert in vertex_data
935            )
936        )
937        me.use_auto_smooth = True
938
939        return ob, me
def create_vertex_groups(self, context, ob, vertex_data, local_bone_data):
941    def create_vertex_groups(self, context, ob, vertex_data, local_bone_data):
942        # 頂点グループ作成
943        for data in local_bone_data:
944            ob.vertex_groups.new(name=common.decode_bone_name(data['name'], self.is_convert_bone_weight_names))
945        context.window_manager.progress_update(4.333)
946
947        for vert_index, data in enumerate(vertex_data):
948            for weight in data['weights']:
949                if 0.0 < weight['value']:
950                    vertex_group = ob.vertex_groups[common.decode_bone_name(weight['name'], self.is_convert_bone_weight_names)]
951                    vertex_group.add([vert_index], weight['value'], 'REPLACE')
952        context.window_manager.progress_update(4.666)
953
954        if self.is_vertex_group_sort:
955            bpy.ops.object.vertex_group_sort(sort_type='NAME')
956
957        if self.is_remove_empty_vertex_group:
958            for vg in ob.vertex_groups[:]:
959                for vert in ob.data.vertices:
960                    for group in vert.groups:
961                        if group.group == vg.index:
962                            if 0.0 < group.weight:
963                                break
964                    else: # if for-loop didn't break
965                        continue
966                    break
967                else: # if for-loop didn't break
968                    ob.vertex_groups.remove(vg)
969        
970        ob.vertex_groups.active_index = 0
def create_uvs(self, context, me, vertex_data, extra_uv_uses):
972    def create_uvs(self, context, me, vertex_data, extra_uv_uses):
973        # UV作成
974        bm = bmesh.new()
975        bm.from_mesh(me)
976        bm.loops.layers.uv.new(f_data_("MainUV"))
977        for i, used in enumerate(extra_uv_uses):    
978            if used:
979                bm.loops.layers.uv.new(f_data_("ExtraUV{num}", num=i))
980        for face in bm.faces:
981            for loop in face.loops:
982                loop[bm.loops.layers.uv[0]].uv = vertex_data[loop.vert.index]['uv']
983                for extra_uv_index, extra_uv in enumerate(vertex_data[loop.vert.index]['extra_uvs']):
984                    loop[bm.loops.layers.uv[extra_uv_index+1]].uv = extra_uv
985        bm.to_mesh(me)
986        bm.free()
def create_shapekeys(self, context, ob, misc_data):
 988    def create_shapekeys(self, context, ob, misc_data):
 989        # モーフ追加
 990        me = ob.data
 991
 992        is_use_attributes = (not compat.IS_LEGACY and bpy.app.version >= (2,92))
 993        is_fast_create = (not compat.IS_LEGACY and bpy.app.version >= (3,2)) 
 994
 995        #if not is_fast_create:
 996        #    bpy.ops.object.mode_set(mode='VERTEX_PAINT')
 997        #    prev_brush_color = context.tool_settings.vertex_paint.brush.color
 998        
 999        vert_loops = dict()
1000        for loop_index, loop in enumerate(me.loops):
1001            vert_loops.setdefault(loop.vertex_index, list()).append(loop_index)
1002        
1003        def fill_color_layer(layer, color):
1004            import numpy as np
1005            color_np = np.array(color, dtype=float)
1006            color_values = np.broadcast_to(color_np, (len(me.loops), len(color_np)))
1007            layer.data.foreach_set('color', color_values.ravel())
1008
1009        def create_normals_color(name):
1010            default_color = (0.5, 0.5, 0.5, 1.0)
1011
1012            #if is_fast_create:
1013            #    bpy.ops.geometry.color_attribute_add(name=name, domain='CORNER', data_type='FLOAT_COLOR', color=default_color)
1014            #    return me.attributes.active
1015            
1016            if is_use_attributes:
1017                normals_color = me.attributes.new(name, 'FLOAT_COLOR', 'CORNER')
1018            else:
1019                normals_color = me.vertex_colors.new(name=name, do_init=False) or me.vertex_colors[-1]
1020
1021            fill_color_layer(normals_color, default_color)
1022            
1023            return normals_color
1024        
1025        def create_unknown_color(data):
1026            unknown_color = None
1027            if len(data['data']) and data['data'][0]['color']:
1028                if is_use_attributes:
1029                    unknown_color = me.attributes.new(f"{data['name']}_unknown", 'FLOAT_COLOR', 'CORNER')
1030                else:
1031                    unknown_color = me.vertex_colors.new(name=f"{data['name']}_unknown", do_init=False) or me.vertex_colors[-1]
1032            return unknown_color
1033
1034        def set_shape_key_data(shape_key, normals_color, unknown_color):
1035            for vert in data['data']:
1036                vert_index = vert['index']
1037                co = compat.convert_cm_to_bl_space( mathutils.Vector(vert['co']) * self.scale )
1038                no = compat.convert_cm_to_bl_space( mathutils.Vector(vert['normal']))
1039                shape_key.data[vert_index].co = shape_key.data[vert_index].co + co
1040
1041                write_vertex_colors(vert, no, normals_color, unknown_color)
1042
1043        def write_vertex_colors(vert, no, normals_color, unknown_color):
1044            for loop_index in vert_loops[vert['index']]:
1045                normals_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
1046                    no[0] * 0.5 + 0.5,
1047                    no[1] * 0.5 + 0.5,
1048                    no[2] * 0.5 + 0.5,
1049                    1,
1050                )
1051                if not vert['color']:
1052                    continue
1053                unknown_color.data[loop_index].color = ( # convert from range(-1, 1) to range(0, 1)
1054                    vert['color'][0] * 0.5 * vert['color'][3] + 0.5,
1055                    vert['color'][1] * 0.5 * vert['color'][3] + 0.5,
1056                    vert['color'][2] * 0.5 * vert['color'][3] + 0.5,
1057                    1,
1058                )
1059
1060        morph_count = -1
1061        for data in misc_data:
1062            if not data['type'] == 'morph':
1063                continue
1064            
1065            morph_count += 1
1066
1067            if morph_count == 0:
1068                bpy.ops.object.shape_key_add(from_mix=False)
1069                me.shape_keys.name = ob.name
1070            shape_key = ob.shape_key_add(name=data['name'], from_mix=False)
1071            
1072            normals_color = create_normals_color(f"{data['name']}_delta_normals")
1073            unknown_color = create_unknown_color(data)
1074            set_shape_key_data(shape_key, normals_color, unknown_color)
def create_mateprop_old(self, context, me, tex_set, mate, mate_idx, data: list):
1077    def create_mateprop_old(self, context, me, tex_set, mate, mate_idx, data: list):
1078        # create_matepropとの違いは、slot_indexの有無、nodeの接続・配置処理のみ
1079
1080        prefs = common.preferences()
1081        # テクスチャ追加
1082        slot_index = 0
1083        for tex_data in data['data']:
1084            if prefs.mate_unread_same_value:
1085                if tex_data['name'] in tex_set:
1086                    continue
1087                tex_set.add(tex_data['name'])
1088
1089            node_name = tex_data['name']
1090            if tex_data['type'] == 'tex':
1091                path = tex_data['path']
1092                tex_map_data = tex_data['tex_map']
1093                common.create_tex(context, mate, node_name, tex_data['name2'], path, path, tex_map_data, prefs.is_replace_cm3d2_tex, slot_index)
1094
1095            elif tex_data['type'] == 'col':
1096                col = tex_data['color']
1097                common.create_col(context, mate, node_name, col, slot_index)
1098
1099            elif tex_data['type'] == 'f':
1100                f = tex_data['float']
1101                common.create_f(context, mate, node_name, f, slot_index)
1102
1103            slot_index += 1
1104
1105            self.progress(context)
def create_mateprop(self, context, me, tex_set, mate, mate_idx, data: list):
1107    def create_mateprop(self, context, me, tex_set, mate, mate_idx, data: list):
1108        if mate.use_nodes is False:
1109            mate.use_nodes = True
1110
1111        nodes = mate.node_tree.nodes
1112        prefs = common.preferences()
1113
1114        for prop_data in data['data']:
1115            if prefs.mate_unread_same_value:
1116                if prop_data['name'] in tex_set:
1117                    continue
1118                tex_set.add(prop_data['name'])
1119
1120            if prop_data['type'] == 'tex':  # テクスチャ追加
1121                prop_name = prop_data['name']
1122                if prop_data['type2'] == 'tex2d':
1123                    tex_name = prop_data['name2']
1124                    cm3d2path = prop_data['path']
1125                    tex_map = prop_data['tex_map']
1126                    tex = common.create_tex(context, mate, prop_name, tex_name, cm3d2path, cm3d2path, tex_map)
1127
1128                    if prop_data['type2'] == 'tex2d':
1129                        mapping = prop_data['tex_map']
1130                        tex_map = tex.texture_mapping
1131                        tex_map.translation[0] = mapping[0]
1132                        tex_map.translation[1] = mapping[1]
1133                        tex_map.scale[0] = mapping[2]
1134                        tex_map.scale[1] = mapping[3]
1135
1136                        # ファイルの実体を割り当て
1137                        if prefs.is_replace_cm3d2_tex:
1138                            img = tex.image
1139                            # col = mate.node_tree.nodes.new(type='ShaderNodeAttribute')
1140                            # tex.image = bpy.data.images.load("C:\\path\\to\\im.jpg")
1141                            replaced = common.replace_cm3d2_tex(img, self.texpath_dict, reload_path=False)
1142                            if compat.IS_LEGACY and replaced and prop_name == '_MainTex':
1143                                for face in me.polygons:
1144                                    if face.material_index == mate_idx:
1145                                        me.uv_textures.active.data[face.index].image = img
1146                else:
1147                    common.create_tex(context, mate, prop_name)
1148
1149            elif prop_data['type'] == 'col':
1150                col = nodes.new(type='ShaderNodeRGB')
1151                col.name = col.label = prop_data['name']
1152                # val.type = 'RGB'
1153                col.outputs[0].default_value = prop_data['color'][:4]
1154
1155                # mate.node_tree.links.new(bsdf.inputs['xxx'], val.outputs['Color'])
1156                # mate.node_tree.nodes.active = col
1157
1158                # slot = mate.texture_slots.create(tex_index)
1159                # mate.use_textures[tex_index] = False
1160                # slot.diffuse_color_factor = tex_data['color'][3]
1161                # slot.use_rgb_to_intensity = True
1162                # tex = context.blend_data.textures.new(tex_data['name'], 'BLEND')
1163                # slot.texture = tex
1164
1165            elif prop_data['type'] == 'f':
1166                val = nodes.new(type='ShaderNodeValue')
1167                val.name = prop_data['name']
1168                val.label = prop_data['name']
1169                # val.type = 'VALUE'
1170                # mate.node_tree.links.new(bsdf.inputs['xxx'], val.outputs['Value'])
1171
1172                val.outputs[0].default_value = prop_data['float']
1173
1174            self.progress(context)
1175
1176        cm3d2_data.align_nodes(mate)
def progress(self, context):
1178    def progress(self, context):
1179        self.progress_count += self.progress_plus_value
1180        context.window_manager.progress_update(self.progress_count)
bl_rna = <bpy_struct, Struct("IMPORT_MESH_OT_import_cm3d2_model")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
bpy_extras.io_utils.ImportHelper
check
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data